abhigyanpatwari/GitNexus · warning · GitNexusRcError

must be a string branch name.

Error message

${source} must be a string branch name.

What it means

Rate-limited sidecar-recovery logging: warnOnce warns on the first occurrence of a condition key and again only at milestone counts (WARN_MILESTONES = [1, 10, 100, 1000, 10000]); non-milestone repeats drop to debug. This message shape — base message plus '(Nth occurrence of this condition)' — means the identical recovery condition has now recurred N times in this process. The underlying conditions are LadybugDB sidecar/WAL recovery events keyed per cause (e.g. the LADYBUGDB-CONTRACT missing-shadow error text, keyed on the locale-invariant Win32 code 2 for Windows).

Solutions

  1. Use the ordinal to gauge severity: 1st occurrence after a crash is expected; 10th+ means a systematic condition
  2. Identify the base condition in the same log line (the message before the parenthetical) and fix its cause — e.g. stop the crash loop, exclude storage dir from AV
  3. Run `gitnexus clean` and a fresh full analyze to reset sidecar state
  4. If the base message mentions shadow-sidecar errors, verify LadybugDB version compatibility (LADYBUGDB-CONTRACT comments in sidecar-recovery.ts)
Defensive patterns

Strategy: fallback

Validate before calling

import fs from 'node:fs/promises';
// before open: detect the missing-shadow precondition that feeds this recovery path
const shadowExists = await fs.access(`${dbPath}.shadow`).then(() => true, () => false);
const walExists = await fs.access(`${dbPath}.wal`).then(() => true, () => false);
if (walExists && !shadowExists) {
  // a recovery event is likely on open — back up / investigate before proceeding
}

Type guard

// LADYBUGDB-CONTRACT shape: native missing-shadow error, locale-invariant on Windows
const isMissingShadowError = (e: unknown): e is Error =>
  e instanceof Error && /Cannot open file.*\.shadow/.test(e.message);

Try / catch

try {
  await openDb(dbPath);
} catch (e) {
  if (isMissingShadowError(e)) {
    // sidecar recovery will quarantine + rename the WAL; let it run, then reopen
    return openDb(dbPath);
  }
  throw e;
}

Prevention

When it happens

Trigger: warnedKeyCounts.get(key) hits a milestone — the same recovery path (missing .shadow sidecar detection, quarantine, rename) fires repeatedly, e.g. every DB open finding the same missing-shadow condition, or a crash loop reopening the store.

Common situations: Persistent crash-then-reopen cycles (CI killing analyze); a storage directory where AV keeps removing/locking shadow sidecars; non-English Windows installs where error matching relies on the invariant error code rather than localized text (issue #2382). The ordinal tells you whether you have a one-off (1st) or a systematic condition (100th).

Related errors


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

Appendix: source

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

  }
}

const normalizeValue = (kind: ValueKind, value: unknown, key: string): unknown => {
  const source = `${GITNEXUS_RC_FILENAME} "${key}"`;
  switch (kind) {
    case 'boolean':
      if (typeof value !== 'boolean') {
        throw new GitNexusRcError(`${source} must be a boolean (true/false).`);
      }
      return value;
    case 'boolean-negate':
      if (typeof value !== 'boolean') {
        throw new GitNexusRcError(`${source} must be a boolean (true/false).`);
      }
      return !value;
    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();

View on GitHub (pinned to ac9a4e9abd)