jackwener/OpenCLI · error · Error
Stale .git/index.lock found — remove it first
Error message
Stale .git/index.lock found — remove it first
What it means
toEnvelope is the CLI's central error-to-envelope mapper in src/errors.ts. Known CliError instances keep their own code/message; anything else falls into this UNKNOWN branch at line 277, where the envelope's message is just whatever getErrorMessage(err) recovered (e.g. err.message or String(err)) and the exit code is EXIT_CODES.GENERIC_ERROR. Hitting this means a non-CliError exception (a raw TypeError, network error, JSON.parse failure, etc.) escaped a command without being wrapped.
Source
Thrown at autoresearch/engine.ts:98
}
private log(msg: string): void {
this.callbacks.onStatus?.(msg);
}
/** Phase 0: Precondition checks */
private checkPreconditions(): void {
// Git repo exists
try { execStrict('git rev-parse --git-dir'); }
catch { throw new Error('Not a git repository'); }
// Clean working tree
const status = exec('git status --porcelain');
if (status) throw new Error(`Working tree not clean:\n${status}`);
// No stale locks
if (existsSync(join(ROOT, '.git', 'index.lock'))) {
throw new Error('Stale .git/index.lock found — remove it first');
}
// Not detached HEAD
try { execStrict('git symbolic-ref HEAD'); }
catch { throw new Error('Detached HEAD — checkout a branch first'); }
}
/** Phase 5: Run verify command and extract metric */
private runVerify(): number | null {
this.log(' verify...');
const output = exec(this.config.verify, { timeout: 300_000 });
return extractMetric(output);
}
/** Phase 5.5: Run guard command */
private runGuard(): boolean {
if (!this.config.guard) return true;
this.log(' guard...');View on GitHub (pinned to 49907e53dc)
Solutions
- Read the envelope's message (and optional cause/trace fields) to identify the underlying raw exception, then fix that root cause.
- Wrap known failure modes in CliError with a specific code so users get actionable codes instead of UNKNOWN.
- Use the trace fields (traceId/summaryPath) if present to inspect what the CLI was doing when the raw error escaped.
- If the message is empty or 'undefined', the thrown value wasn't an Error — add logging of the raw thrown value in the command path.
Example fix
// before
const data = JSON.parse(raw); // raw TypeError escapes as UNKNOWN
// after
let data;
try {
data = JSON.parse(raw);
} catch (err) {
throw new CliError('PARSE_ERROR', `Invalid JSON: ${getErrorMessage(err)}`);
} Defensive patterns
Strategy: try-catch
Validate before calling
if (err instanceof CliError) {
// known, coded failure — envelope preserves err.code
} else {
// will surface as code 'UNKNOWN'; log raw value now before mapping
console.error('raw thrown value:', typeof err, err);
} Type guard
function isCliError(err: unknown): err is CliError {
return err instanceof CliError;
} Try / catch
try {
await runCommand(argv);
} catch (err) {
const envelope = toEnvelope(err);
if (envelope.error.code === 'UNKNOWN') {
logger.debug({ raw: err }, 'unwrapped error surfaced as UNKNOWN');
}
process.exitCode = envelope.error.exitCode;
console.error(JSON.stringify(envelope, null, 2));
} Prevention
- Wrap every anticipated failure mode in CliError with a specific code at the layer where it occurs.
- Never throw raw strings or non-Error values; always throw Error subclasses so messages survive getErrorMessage.
- Keep the cause chain (new Error(msg, { cause })) so the envelope's cause field aids debugging.
- Review any UNKNOWN envelope in CI logs as a signal that a command path is missing error wrapping.
When it happens
Trigger: Any command path that lets a plain Error (or non-Error value) reach the top-level envelope() call instead of throwing a CliError — e.g. unhandled TypeError inside a command, JSON.parse failure, uncaught promise rejection from a dependency.
Common situations: A library call throws a native error the CLI forgot to wrap; a bug (undefined property access) surfaces in production; an async callback rejects outside the CliError-wrapping layer; a non-Error value like a string is thrown.
Related errors
- Not a git repository
- Working tree not clean: ${status}
- 12306 whoami failed: ${probe.detail}
- <train-no> "${trainNo}" does not look like a 12306 internal
- --from station must not be empty
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/26d1404ba3f39593.
Report an issue: GitHub.