abhigyanpatwari/GitNexus · error · GitNexusRcError

must be a string.

Error message

${source} must be a string.

What it means

A manual WAL checkpoint hit an IO error and the retry driver exhausted its budget (CHECKPOINT_RETRY_ATTEMPTS = 3 attempts, backoff from BASE_DELAYS_MS plus jitter so concurrent analyzers do not retry in lockstep). The original IO error is rethrown un-wrapped so the CLI layer (analyze.ts) can classify the held-open cause (#2599) and render the --wal-checkpoint-threshold recovery hint. The run fails this checkpoint path rather than corrupting WAL state.

Solutions

  1. Close concurrent holders of the DB (stop `gitnexus serve`, IDE integrations, other analyzers) and rerun
  2. Free disk space — checkpoint IO errors often accompany a full volume
  3. Exclude the GitNexus storage directory from antivirus scanning
  4. Tune cadence via --wal-checkpoint-threshold (e.g. raise it to reduce rename churn, per the recovery hint analyze.ts renders)
Defensive patterns

Strategy: retry

Validate before calling

import fs from 'node:fs/promises';
// before a manual checkpoint: cheap preflight on the two common causes
await fs.access(dbPath); // surfaces ENOSPC path issues early
const stat = await fs.statfs(path.dirname(dbPath)).catch(() => null);
if (stat && stat.bavail * stat.bsize < 512 * 1024 * 1024) {
  throw new Error('insufficient disk space for WAL checkpoint');
}

Type guard

const isLbugIoError = (e: unknown): e is Error =>
  e instanceof Error && /IO error/i.test(e.message);

Try / catch

try {
  await checkpointWithRetry(driver);
} catch (e) {
  if (isLbugIoError(e)) {
    // retry budget (3 attempts, backoff+jitter) is exhausted; the error reaches here
    // unwrapped so the held-open classifier in analyze.ts can name the cause —
    // stop concurrent DB users, free disk, then re-run analyze
    throw e;
  }
  throw e;
}

Prevention

When it happens

Trigger: CHECKPOINT wal; returns an IO error three times in a row — another process/reader holding the DB file open (the #2599 held-open cause), a full or failing disk, or AV interference on the WAL/sidecar files during the rename/remove churn of checkpointing.

Common situations: Concurrent `gitnexus serve` / IDE extension holding the DB while analyze tries to checkpoint; disk-full on CI; aggressive antivirus locking .wal/.shadow during rename; network storage dropping writes. Followed by the analyze-layer error with the recovery hint.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@ac9a4e9abd (2026-09-01). Data as JSON: /api/errors/7aaee73a0bb7b6b6. Report an issue: GitHub.

Appendix: source

Thrown at gitnexus/src/cli/analyze-config.ts:220

    case 'branch':
      if (typeof value !== 'string') {
        throw new GitNexusRcError(`${source} must be a string branch name.`);
      }
      return validateBranchName(value, source);
    case 'path': {
      if (typeof value !== 'string') {
        throw new GitNexusRcError(`${source} must be a file or directory path.`);
      }
      const trimmed = value.trim();
      if (!trimmed) {
        throw new GitNexusRcError(`${source} must not be empty.`);
      }
      assertNoHiddenChars(trimmed, source);
      return trimmed;
    }
    case 'string': {
      if (typeof value !== 'string') {
        throw new GitNexusRcError(`${source} must be a string.`);
      }
      const trimmed = value.trim();
      if (!trimmed) {
        throw new GitNexusRcError(`${source} must not be empty.`);
      }
      assertNoHiddenChars(trimmed, source);
      // `name` flows into the generated AGENTS.md/CLAUDE.md as `**${name}**` and
      // inside `gitnexus://repo/${name}/…` code spans, so a Markdown-significant
      // character would break those spans or inject emphasis/links/HTML into
      // agent-instruction content (#1996 tri-review P1). `_` is intentionally
      // allowed (legitimate in repo names; intraword `_` is not emphasis).
      // embeddingDevice (the other `string`-kind option) only ever holds a
      // fixed device token, so this guard never rejects a valid value there.
      if (/[`*[\]<>]/.test(trimmed)) {
        throw new GitNexusRcError(
          `${source} must not contain Markdown-significant characters (\` * [ ] < >).`,
        );
      }

View on GitHub (pinned to ac9a4e9abd)