◐ Off-By-One · answer catalog

typescript-cli-error-handling

2 answer(s)godockergodocker

typescript-cli-error-handling

📦 Source in repository (JSON)

Answer 1

The Mythos CLI had three related defects in the batch path, all reproduced and fixed in ~/mythos-cli (commits abb91ab → 9f667cb):

Before — mythos batch outside a project:

[provider:fs] version=1.0.0 module=/tmp/mythos-repro/outside-project/providers/fs.js   ← absolute path leak
Fatal error: No project loaded: cannot find mythos.json in /tmp/... or any parent.      ← bare, no guidance
EXIT=2                                                                                   ← wrong exit code

1. Gate ProviderRegistry diagnostics behind verbosity — the registry's diagnostics include absolute on-disk module paths, so they're only emitted when the user opted in (--verbose or MYTHOS_VERBOSE=1):

// src/provider.ts
private logDiagnostics(verbose: boolean): void {
  if (!verbose) return;                    // gate: default runs never leak paths
  for (const p of this.providers.values()) {
    console.error(`[provider:${p.name}] version=${p.version} module=${p.modulePath}`);
  }
}
initialize(verbose: boolean): void { this.logDiagnostics(verbose); }

2. Propagate MYTHOS_VERBOSE to backend services — spawned children get an explicit normalized env var (never stale/missing), from the CLI's resolved verbosity (--verbose wins over env fallback via src/config.ts):

// src/backend.ts
export function spawnBackend(command: string, args: string[], opts: BackendOptions): Promise<number> {
  const env: NodeJS.ProcessEnv = { ...process.env, MYTHOS_VERBOSE: opts.verbose ? '1' : '0' };
  return new Promise((resolve, reject) => {
    const child = spawn(command, args, { env, stdio: 'inherit' });
    child.on('error', reject);
    child.on('exit', (code) => resolve(code ?? 1));
  });
}

3. Remap StorageError in the CLI catch-all + exit 1 — instead of a bare Fatal error: line, the catch-all detects the storage failure and gives actionable guidance, always exiting 1:

// src/cli.ts — main().catch(...)
if (e instanceof StorageError || e.name === 'StorageError') {
  console.error(
    `mythos: ${e.message}\n` +
    'No project is loaded in this directory.\n' +
    'Run `mythos init` to create a project here, then try again.',
  );
} else {
  console.error(`Fatal error: ${e.message}`);   // unrelated errors keep the prefix
}
process.exitCode = 1;

After — mythos batch outside a project:

mythos: No project loaded: cannot find mythos.json in /tmp/... or any parent directory.
No project is loaded in this directory.
Run `mythos init` to create a project here, then try again.
EXIT=1

Evidence & signatures

Reproduced the bug against the committed buggy baseline (`git worktree` at `abb91ab`), applied the fix, and verified with a child-process harness (`test/run-tests.mjs`, 21 assertions) run via `npm test`:

| Check | Buggy | Fixed |
|---|---|---|
| Harness result | **10 failed** | **21/21 passed** |
| Outside-project exit code | 2 | 1 |
| `mythos init` hint on stderr | missing | present |
| Absolute provider path (`/providers/fs.js`) leak | leaked | absent by default, shown only under `--verbose`/`MYTHOS_VERBOSE=1` |
| Backend service sees `MYTHOS_VERBOSE` | undefined | `1` with flag, `0` without |
| In-project `init`+`batch` | n/a | exit 0, "Batch finished" |
| Unrelated fatal (unknown command) | exit 2 | exit 1, `Fatal error:` prefix kept |

Edge cases covered: env-only verbosity (`MYTHOS_VERBOSE=1` without flag gates diagnostics on — T5), stale parent env normalized to explicit `1`/`0` rather than pass-through (T7), verbose outside-project still exits 1 with hint (T2), StorageError detection via `name` as well as `instanceof` to survive module duplication. Verified from clean state: `rm -rf dist && npm test` → `21 passed, 0 failed`.
{"model": "deepseek-v4-flash", "problem_class": "typescript-cli-error-handling", "result": "passed", "tests": 21}

Answer 2

The Mythos CLI had three related defects in the batch path, all reproduced and fixed in ~/mythos-cli (commits abb91ab → 9f667cb):

Before — mythos batch outside a project:

[provider:fs] version=1.0.0 module=/tmp/mythos-repro/outside-project/providers/fs.js   ← absolute path leak
Fatal error: No project loaded: cannot find mythos.json in /tmp/... or any parent.      ← bare, no guidance
EXIT=2                                                                                   ← wrong exit code

1. Gate ProviderRegistry diagnostics behind verbosity — the registry's diagnostics include absolute on-disk module paths, so they're only emitted when the user opted in (--verbose or MYTHOS_VERBOSE=1):

// src/provider.ts
private logDiagnostics(verbose: boolean): void {
  if (!verbose) return;                    // gate: default runs never leak paths
  for (const p of this.providers.values()) {
    console.error(`[provider:${p.name}] version=${p.version} module=${p.modulePath}`);
  }
}
initialize(verbose: boolean): void { this.logDiagnostics(verbose); }

2. Propagate MYTHOS_VERBOSE to backend services — spawned children get an explicit normalized env var (never stale/missing), from the CLI's resolved verbosity (--verbose wins over env fallback via src/config.ts):

// src/backend.ts
export function spawnBackend(command: string, args: string[], opts: BackendOptions): Promise<number> {
  const env: NodeJS.ProcessEnv = { ...process.env, MYTHOS_VERBOSE: opts.verbose ? '1' : '0' };
  return new Promise((resolve, reject) => {
    const child = spawn(command, args, { env, stdio: 'inherit' });
    child.on('error', reject);
    child.on('exit', (code) => resolve(code ?? 1));
  });
}

3. Remap StorageError in the CLI catch-all + exit 1 — instead of a bare Fatal error: line, the catch-all detects the storage failure and gives actionable guidance, always exiting 1:

// src/cli.ts — main().catch(...)
if (e instanceof StorageError || e.name === 'StorageError') {
  console.error(
    `mythos: ${e.message}\n` +
    'No project is loaded in this directory.\n' +
    'Run `mythos init` to create a project here, then try again.',
  );
} else {
  console.error(`Fatal error: ${e.message}`);   // unrelated errors keep the prefix
}
process.exitCode = 1;

After — mythos batch outside a project:

mythos: No project loaded: cannot find mythos.json in /tmp/... or any parent directory.
No project is loaded in this directory.
Run `mythos init` to create a project here, then try again.
EXIT=1

Evidence & signatures

Reproduced the bug against the committed buggy baseline (`git worktree` at `abb91ab`), applied the fix, and verified with a child-process harness (`test/run-tests.mjs`, 21 assertions) run via `npm test`:

| Check | Buggy | Fixed |
|---|---|---|
| Harness result | **10 failed** | **21/21 passed** |
| Outside-project exit code | 2 | 1 |
| `mythos init` hint on stderr | missing | present |
| Absolute provider path (`/providers/fs.js`) leak | leaked | absent by default, shown only under `--verbose`/`MYTHOS_VERBOSE=1` |
| Backend service sees `MYTHOS_VERBOSE` | undefined | `1` with flag, `0` without |
| In-project `init`+`batch` | n/a | exit 0, "Batch finished" |
| Unrelated fatal (unknown command) | exit 2 | exit 1, `Fatal error:` prefix kept |

Edge cases covered: env-only verbosity (`MYTHOS_VERBOSE=1` without flag gates diagnostics on — T5), stale parent env normalized to explicit `1`/`0` rather than pass-through (T7), verbose outside-project still exits 1 with hint (T2), StorageError detection via `name` as well as `instanceof` to survive module duplication. Verified from clean state: `rm -rf dist && npm test` → `21 passed, 0 failed`.
{"model": "deepseek-v4-flash", "problem_class": "typescript-cli-error-handling", "result": "passed", "tests": 21}
Generated from the verified corpus · MIT licensedBack to the catalog