{"record":{"id":"7aaee73a0bb7b6b6","repo":"abhigyanpatwari/GitNexus","slug":"source-must-be-a-string","errorCode":null,"errorMessage":"${source} must be a string.","messagePattern":"(.+?) must be a string\\.","errorType":"validation","errorClass":"GitNexusRcError","httpStatus":null,"severity":"error","filePath":"gitnexus/src/cli/analyze-config.ts","lineNumber":220,"sourceCode":"    case 'branch':\n      if (typeof value !== 'string') {\n        throw new GitNexusRcError(`${source} must be a string branch name.`);\n      }\n      return validateBranchName(value, source);\n    case 'path': {\n      if (typeof value !== 'string') {\n        throw new GitNexusRcError(`${source} must be a file or directory path.`);\n      }\n      const trimmed = value.trim();\n      if (!trimmed) {\n        throw new GitNexusRcError(`${source} must not be empty.`);\n      }\n      assertNoHiddenChars(trimmed, source);\n      return trimmed;\n    }\n    case 'string': {\n      if (typeof value !== 'string') {\n        throw new GitNexusRcError(`${source} must be a string.`);\n      }\n      const trimmed = value.trim();\n      if (!trimmed) {\n        throw new GitNexusRcError(`${source} must not be empty.`);\n      }\n      assertNoHiddenChars(trimmed, source);\n      // `name` flows into the generated AGENTS.md/CLAUDE.md as `**${name}**` and\n      // inside `gitnexus://repo/${name}/…` code spans, so a Markdown-significant\n      // character would break those spans or inject emphasis/links/HTML into\n      // agent-instruction content (#1996 tri-review P1). `_` is intentionally\n      // allowed (legitimate in repo names; intraword `_` is not emphasis).\n      // embeddingDevice (the other `string`-kind option) only ever holds a\n      // fixed device token, so this guard never rejects a valid value there.\n      if (/[`*[\\]<>]/.test(trimmed)) {\n        throw new GitNexusRcError(\n          `${source} must not contain Markdown-significant characters (\\` * [ ] < >).`,\n        );\n      }","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/ac9a4e9abd8fd3058c070b72c23402a4f887929a/gitnexus/src/cli/analyze-config.ts#L202-L238","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Close concurrent holders of the DB (stop `gitnexus serve`, IDE integrations, other analyzers) and rerun","Free disk space — checkpoint IO errors often accompany a full volume","Exclude the GitNexus storage directory from antivirus scanning","Tune cadence via --wal-checkpoint-threshold (e.g. raise it to reduce rename churn, per the recovery hint analyze.ts renders)"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"import fs from 'node:fs/promises';\n// before a manual checkpoint: cheap preflight on the two common causes\nawait fs.access(dbPath); // surfaces ENOSPC path issues early\nconst stat = await fs.statfs(path.dirname(dbPath)).catch(() => null);\nif (stat && stat.bavail * stat.bsize < 512 * 1024 * 1024) {\n  throw new Error('insufficient disk space for WAL checkpoint');\n}","typeGuard":"const isLbugIoError = (e: unknown): e is Error =>\n  e instanceof Error && /IO error/i.test(e.message);","tryCatchPattern":"try {\n  await checkpointWithRetry(driver);\n} catch (e) {\n  if (isLbugIoError(e)) {\n    // retry budget (3 attempts, backoff+jitter) is exhausted; the error reaches here\n    // unwrapped so the held-open classifier in analyze.ts can name the cause —\n    // stop concurrent DB users, free disk, then re-run analyze\n    throw e;\n  }\n  throw e;\n}","preventionTips":["Stop `gitnexus serve` / IDE extensions before running a full analyze on the same storage","Keep free disk headroom above the WAL size before checkpoint-heavy phases","AV-exclude the storage directory to stop IO errors during WAL rename/remove churn"],"tags":["wal","checkpoint","io-error","retry","ladybugdb"],"backgroundTag":"wal-checkpoint-io-error","analyzedSha":"ac9a4e9abd8fd3058c070b72c23402a4f887929a","analyzedAt":"2026-09-01T13:15:02.810Z","contentChangedAt":"2026-09-01T13:15:02.810Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}