How to Troubleshoot "Claude CLI Not Found" Errors in Claudian: A Complete Guide

Claudian resolves the Claude CLI through a five-step resolution chain that checks per-host paths, legacy settings, environment variables, and built-in fallback directories, returning "not found" only when all checks fail.

When the Obsidian plugin Claudian cannot locate the Claude CLI executable, it typically stems from misconfigured host-specific paths or the executable residing outside the default search directories. Understanding the resolution hierarchy implemented in src/utils/claudeCli.ts allows you to diagnose exactly where the lookup fails. This guide explains the detection mechanism used by the YishenTu/claudian repository and provides concrete steps to resolve path issues.

How Claudian Resolves the Claude CLI Path

The ClaudeCliResolver class in src/utils/claudeCli.ts implements a prioritized resolution chain. When you trigger a Claude command, the resolver checks locations in the following order until it finds a valid executable file.

1. Per-Host Path Configuration

The resolver first checks claudeCliPathsByHost[hostname] using the key returned by getHostnameKey() from src/utils/env.ts. This allows different devices to maintain separate CLI paths without conflicting settings.

According to the source code at lines 27-35 of src/utils/claudeCli.ts, the resolver looks up the current hostname in the claudeCliPathsByHost map stored in claudian-settings.json. If a match exists and the file is accessible, resolution stops here.

2. Legacy Single-Path Field

If no per-host entry exists, the system falls back to the legacy claudeCliPath field for backward compatibility. As implemented in src/utils/claudeCli.ts (lines 36-43), this field accepts a single string path but is ignored if the per-host map contains an entry for the current machine.

3. Environment Variable Override

When both settings fields are empty or invalid, the resolver checks for a custom PATH environment variable. The resolveClaudeCliPath function calls findClaudeCLIPath(customEnv.PATH) at lines 68-73 of src/utils/claudeCli.ts, allowing you to specify alternative binary directories without modifying system PATH.

The findClaudeCLIPath function in src/utils/path.ts (lines 64-82 and 88-115) performs an exhaustive scan of well-known installation locations. This includes:

  • Home directory binaries (~/.claude/local/, ~/.local/bin/)
  • npm global prefixes (/usr/local/lib/node_modules/, %APPDATA%/npm/)
  • Windows Program Files directories
  • NVM (Node Version Manager) binary paths

5. OS-Specific Executable Names

Finally, the resolver adapts to operating system conventions. On Windows, it prefers claude.exe then cli.js; on Unix systems, it checks for the claude binary before falling back to cli.js for npm-based installations (lines 77-89 and 96-104 of src/utils/path.ts).

Common Reasons for Resolution Failures

Symptom Root Cause Verification Method
Null result despite installation Executable located outside all searched directories Inspect process.env.PATH in the DevTools console or run node -e "console.log(process.env.PATH)"
Per-host entry ignored Hostname key mismatch between getHostnameKey() output and settings JSON Log the return value of getHostnameKey() and compare against keys in claudeCliPathsByHost
Legacy path persistent claudeCliPath populated while claudeCliPathsByHost is empty, pointing to a deleted file Clear the claudeCliPath field in claudian-settings.json
Unexpanded variables Path contains ~ or %VAR% that expandHomePath failed to resolve Test expansion manually: node -e "console.log(require('./src/utils/path').expandHomePath('~/.local/bin/claude'))"
Broken symlinks fs.statSync detects the symlink but the target executable is missing Run ls -l <path> on Unix or Get-Item on PowerShell to verify symlink integrity

Step-by-Step Troubleshooting Guide

Follow this diagnostic sequence to identify why the "Claude CLI not found" error appears:

  1. Inspect your settings file located at .claudian-settings.json in your vault root. Verify the structure contains either a valid claudeCliPathsByHost entry or an accurate claudeCliPath:

    {
      "claudeCliPath": "",
      "claudeCliPathsByHost": {
        "my-hostname": "/usr/local/bin/claude"
      }
    }
  2. Validate the hostname key by checking the output of getHostnameKey() from src/utils/env.ts. If your machine name changed or the key differs from your settings entry, update the JSON key to match.

  3. Test auto-detection manually by opening the Obsidian Developer Console (Ctrl+Shift+I) and executing:

    const { findClaudeCLIPath } = require('./src/utils/path.js');
    console.log(findClaudeCLIPath(process.env.PATH));

    If this returns null, the CLI is not in any standard location.

  4. Check fallback directories manually to confirm installation location:

    # macOS/Linux
    
    ls -l ~/.claude/local/claude /usr/local/bin/claude ~/.local/bin/claude
    
    # Windows PowerShell
    
    Get-Item "$HOME\.claude\local\claude.exe", "$env:ProgramFiles\Claude\claude.exe"
  5. Verify npm installations by checking for cli.js under global npm prefixes returned by getNpmCliJsPaths() in src/utils/path.ts (lines 47-62).

  6. Restart Obsidian after correcting paths to ensure ClaudeCliResolver clears its internal cache (the reset() method is called on plugin unload as seen in src/main.ts lines 258-276).

Programmatic Debugging Examples

Use these code snippets to debug resolution logic directly within the Claudian plugin environment.

Using the Resolver Programmatically

import { ClaudeCliResolver } from '@/utils/claudeCli';
import type { HostnameCliPaths } from '@/core/types/settings';
import { getHostnameKey } from '@/utils/env';

const resolver = new ClaudeCliResolver();

function debugCliResolution(settings: any): string | null {
  const hostKey = getHostnameKey();
  const hostPaths: HostnameCliPaths = settings.claudeCliPathsByHost;
  const legacy = settings.claudeCliPath;
  const envText = process.env.CUSTOM_PATH || ''; // Empty uses real process.env
  
  console.log(`Resolving for host: ${hostKey}`);
  console.log(`Host paths map:`, hostPaths);
  console.log(`Legacy path:`, legacy);
  
  return resolver.resolve(hostPaths, legacy, envText);
}

Manual Auto-Detection Test

import { findClaudeCLIPath } from '@/utils/path';

// Test with current PATH
const detectedPath = findClaudeCLIPath(process.env.PATH);
console.log(`Detected CLI: ${detectedPath ?? 'Not found'}`);

// Test with custom PATH
const customPath = '/opt/claude/bin:/usr/local/bin';
console.log(findClaudeCLIPath(customPath));

Fixing Stale Hostname Entries

// Get current hostname key
const currentHost = require('./src/utils/env.js').getHostnameKey();

// Migrate settings
if (plugin.settings.claudeCliPathsByHost['old-laptop-name']) {
  plugin.settings.claudeCliPathsByHost[currentHost] = 
    plugin.settings.claudeCliPathsByHost['old-laptop-name'];
  delete plugin.settings.claudeCliPathsByHost['old-laptop-name'];
  await plugin.saveSettings();
}

Key Source Files for Reference

Understanding these files helps you trace resolution logic:

  • src/utils/claudeCli.ts – Contains ClaudeCliResolver and resolveClaudeCliPath, the primary entry points for CLI resolution (lines 13-53).
  • src/utils/path.ts – Implements findClaudeCLIPath with exhaustive default locations and OS-specific handling (lines 64-115).
  • src/utils/env.ts – Provides getHostnameKey() for per-host lookups (lines 440-449).
  • src/core/types/settings.ts – Declares claudeCliPath (legacy) and claudeCliPathsByHost (preferred) TypeScript interfaces (lines 282-283).
  • src/main.ts – Initializes the resolver on plugin load and handles cache reset on shutdown (lines 258-276).

Summary

  • Claudian checks five locations in strict order: per-host map, legacy path, custom environment, built-in directories, and OS-specific fallbacks.
  • Hostname mismatches are the most common cause of per-host configuration failures; verify with getHostnameKey().
  • Auto-detection scans home directories, npm globals, and standard binary paths, but misses custom installation directories outside these locations.
  • Always restart Obsidian after modifying CLI paths to clear the resolver cache.
  • Debug programmatically using findClaudeCLIPath() and ClaudeCliResolver to see exactly which checks fail.

Frequently Asked Questions

Why does Claudian say "Claude CLI not found" when I can run claude in my terminal?

The plugin uses a specific resolution chain that may not include your CLI's directory if it is located in a non-standard path or added to shell-specific configuration files (like .bashrc) that Obsidian does not load. Run findClaudeCLIPath(process.env.PATH) in the DevTools console to see if the plugin detects your PATH correctly, or manually specify the full path in claudeCliPathsByHost using your hostname key.

How do I find my hostname key for the per-host path setting?

The hostname key is generated by getHostnameKey() in src/utils/env.ts. To view your current key, open the Obsidian Developer Console and execute require('./src/utils/env.js').getHostnameKey(). Use this exact string as the key in your claudeCliPathsByHost JSON object.

Can I use environment variables like $HOME or %USERPROFILE% in the path settings?

The resolution system attempts to expand paths using expandHomePath and parsePathEntries from src/utils/path.ts. While tilde (~) expansion is supported, complex environment variable syntax may fail. Use absolute paths when possible, or test expansion manually by importing the path utilities and verifying the output before saving settings.

What is the difference between claudeCliPath and claudeCliPathsByHost?

claudeCliPath is a legacy single-string setting maintained for backward compatibility, while claudeCliPathsByHost is a map object allowing different paths for different machines. According to src/core/types/settings.ts, the per-host map takes precedence; if an entry exists for the current hostname, the legacy field is ignored entirely.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →