abhigyanpatwari/GitNexus · warning · GitNexusRcError

: value contains control or hidden/bidirectional…

Error message

${source}: value contains control or hidden/bidirectional characters, which are not allowed.

What it means

On DB open, GitNexus found WAL files that LadybugDB had quarantined (renamed to `${dbPath}.wal.missing-shadow.<ts>-<rand>` by quarantineWalForMissingShadow) after an unrecoverable prior crash, and deleted them. Quarantined WALs are permanently detached from the live store and never reopened, so removal is safe regardless of whether the main DB exists. A reclaim failure (e.g. transient EBUSY from antivirus) never blocks DB startup.

Solutions

  1. Treat a single occurrence after a crash as expected recovery — no action required
  2. If it repeats, capture why runs crash in the first place (OOM, SIGKILL, disk full) and fix that root cause
  3. Add the GitNexus storage directory to antivirus exclusions so sidecars are not locked/removed mid-run
  4. Run `gitnexus clean` and re-index if you suspect accumulated crash debris in the storage dir
Defensive patterns

Strategy: fallback

Validate before calling

import fs from 'node:fs/promises';
import path from 'node:path';
// before opening the DB: detect leftover quarantines from a prior crash
const dir = path.dirname(dbPath);
const quarantined = (await fs.readdir(dir).catch(() => []))
  .filter((f) => f.includes('.wal.missing-shadow.'));
if (quarantined.length > 0) {
  // expect reclaim warns on open; investigate what crashed the previous run
}

Prevention

When it happens

Trigger: cleanQuarantinedMissingShadowWals(dbPath) returns files matching the quarantine rename pattern — i.e. a previous run crashed in a way where the WAL's shadow sidecar was missing and LadybugDB could not recover, so it set the WAL aside instead of deleting it.

Common situations: Power loss or forced kill during heavy write load (the #1618-style crash loop); antivirus quarantining or locking a .shadow sidecar mid-checkpoint so the next open finds the WAL unrecoverable; repeated occurrences point at systematic crash-then-recover cycles on the same machine.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@ac9a4e9abd (2026-08-20). Data as JSON: /api/errors/8eab48b20b7e92a1. Report an issue: GitHub.

Appendix: source

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

 * Reject control characters and hidden / bidirectional Unicode in a string
 * value. These have no legitimate place in a branch name, registry name, or
 * device string, and would otherwise let a committed config smuggle invisible
 * controls into generated AGENTS.md / CLAUDE.md content.
 */
const isHiddenOrControl = (codePoint: number): boolean =>
  codePoint < 0x20 ||
  codePoint === 0x7f ||
  (codePoint >= 0x200b && codePoint <= 0x200f) || // zero-width + LRM/RLM
  (codePoint >= 0x202a && codePoint <= 0x202e) || // bidi embeddings/overrides
  (codePoint >= 0x2060 && codePoint <= 0x2064) || // word-joiner + invisible math
  (codePoint >= 0x2066 && codePoint <= 0x206f) || // bidi isolates + deprecated
  codePoint === 0xfeff; // BOM / zero-width no-break space

const assertNoHiddenChars = (value: string, source: string): void => {
  for (const ch of value) {
    const cp = ch.codePointAt(0);
    if (cp !== undefined && isHiddenOrControl(cp)) {
      throw new GitNexusRcError(
        `${source}: value contains control or hidden/bidirectional characters, which are not allowed.`,
      );
    }
  }
};

/**
 * Validate a user-supplied branch name (from CLI or `.gitnexusrc`). Returns the
 * trimmed name or throws {@link GitNexusRcError}. Rules live in
 * `core/git-ref.ts`; this wrapper keeps the CLI / `.gitnexusrc` error type.
 */
export function validateBranchName(value: string, source: string): string {
  try {
    return validateBranchNameCore(value, source);
  } catch (err) {
    if (err instanceof InvalidBranchError) {
      throw new GitNexusRcError(err.message);
    }

View on GitHub (pinned to ac9a4e9abd)