charmbracelet/crush · error

interpreter %q not found and %q not in PATH

Error message

interpreter %q not found and %q not in PATH

What it means

resolveInterpreter reports that the shebang's literal interpreter path does not exist AND its basename cannot be found on PATH. This is the two-part variant of the bare-name lookup failure: the resolver tried os.Stat on the literal path (got fs.ErrNotExist), then exec.LookPath on the basename, and both failed.

Source

Thrown at internal/shell/dispatch.go:246

	if statErr == nil {
		return path, nil
	}
	if !errors.Is(statErr, fs.ErrNotExist) {
		return "", statErr
	}

	base := filepath.Base(path)
	if base == "" || base == path && !strings.ContainsAny(path, `/\`) {
		// Already a bare name — just do a PATH lookup.
		resolved, err := exec.LookPath(path)
		if err != nil {
			return "", fmt.Errorf("interpreter %q not found in PATH", path)
		}
		return resolved, nil
	}
	resolved, err := exec.LookPath(base)
	if err != nil {
		return "", fmt.Errorf("interpreter %q not found and %q not in PATH", path, base)
	}
	slog.Debug("Shebang interpreter not found; falling back to PATH",
		"requested", path, "resolved", resolved)
	return resolved, nil
}

// shebang captures the parsed `#!` line. interpreter is the program to
// invoke; args is the list of extra arguments to pass before the script
// path. The kernel's single-arg semantics (for literal paths and for env
// without `-S`) is encoded by returning a single-element args slice
// containing the un-tokenized remainder.
type shebang struct {
	interpreter string
	args        []string
}

// parseShebang extracts the interpreter invocation from probe. It tolerates
// CRLF line endings and a single leading space between `#!` and the path.

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Install the interpreter so the shebang path exists, or place its basename on PATH.
  2. Edit the shebang to reference an interpreter that exists on the current machine.
  3. For portability, use `#!/usr/bin/env <name>` form so the resolver can PATH-lookup the name directly.
  4. On Windows, install Git for Windows and add its usr/bin (bash.exe) to PATH.

Example fix

// before
#!/usr/local/bin/python3
print('hi')
// after
#!/usr/bin/env python3
print('hi')
Defensive patterns

Strategy: validation

Validate before calling

interp := "/usr/local/bin/python3"
if _, err := os.Stat(interp); errors.Is(err, fs.ErrNotExist) {
    if _, err := exec.LookPath(filepath.Base(interp)); err != nil {
        // neither path nor PATH has it — fix environment first
    }
}

Try / catch

var pe *fs.PathError
if errors.As(err, &pe) || strings.Contains(err.Error(), "not found and") {
    // surface a friendly 'install <interpreter>' message
}

Prevention

When it happens

Trigger: A script's shebang uses a full path (contains '/' or '\'), e.g. `#!/usr/local/bin/python3`; os.Stat returns fs.ErrNotExist (so EACCES etc. are surfaced elsewhere), and exec.LookPath("python3") also fails. Raised by dispatchShebang for every such script.

Common situations: Scripts copied between machines where the interpreter lives at different paths; Docker images without /usr/local/bin/python3; Windows hosts lacking any PATH-resolvable bash for `#!/bin/bash`; uninstalling an interpreter after scripts referencing it were authored.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/73e37321de041d09. Report an issue: GitHub.