paperclipai/paperclip · error · Error

codex auth cache: ${label} is empty

Error message

codex auth cache: ${label} is empty

What it means

Thrown by toSafePathSegment in the codex auth cache when a value used as a path segment is empty after trimming. The cache stores per-identity Codex credentials under companies/<companyId>/codex-auth-cache/<accountId>/auth.json, so each segment must be a real basename; an empty value would collapse the path and is treated as a fail-loud security defect (Security condition 3).

Source

Thrown at packages/adapters/codex-local/src/server/codex-auth-cache.ts:72

 * (`0`, `false`, `no`, or `off`).
 */
export function isCodexAuthCacheEnabled(env: NodeJS.ProcessEnv = process.env): boolean {
  const raw = env[CODEX_AUTH_CACHE_OFF_SWITCH_ENV];
  if (typeof raw !== "string") return true;
  return !FALSY_ENV_RE.test(raw.trim());
}

/**
 * Sanitizes one raw value to a single safe path segment. Rejects an empty value,
 * a relative segment (`.` or `..`), a path separator (`/` or `\`), and a NUL
 * byte, so the value can never become a path traversal. Returns the trimmed,
 * safe segment. The `label` names the value in the error message. (Security
 * condition 3.)
 */
function toSafePathSegment(value: string, label: string): string {
  const trimmed = typeof value === "string" ? value.trim() : "";
  if (trimmed.length === 0) {
    throw new Error(`codex auth cache: ${label} is empty`);
  }
  if (trimmed === "." || trimmed === "..") {
    throw new Error(`codex auth cache: ${label} is a relative path segment`);
  }
  if (trimmed.includes("/") || trimmed.includes("\\") || trimmed.includes("\0")) {
    throw new Error(`codex auth cache: ${label} contains a path separator`);
  }
  // Defense in depth: a safe segment is exactly its own basename. Anything else
  // carries a separator or a relative segment the checks above must have caught.
  if (path.basename(trimmed) !== trimmed) {
    throw new Error(`codex auth cache: ${label} is not a single path segment`);
  }
  return trimmed;
}

/**
 * Sanitizes an `account_id` to one safe path segment. Rejects an empty value, a
 * relative segment (`.` or `..`), a path separator, and a NUL byte, so a raw

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Ensure the Codex auth.json being cached has a non-empty tokens.account_id (re-run codex login so the account id is populated).
  2. Ensure the companyId passed to resolveCodexAuthCacheDir is a real, non-empty company id.
  3. If you intentionally have API-key-only credentials with no account_id, disable the cache via PAPERCLIP_CODEX_AUTH_CACHE=0 so the vend path is skipped.

Example fix

// before
toCacheKey(auth.tokens?.account_id ?? "")
// after — guard before caching
const accountId = auth.tokens?.account_id;
if (accountId && accountId.trim()) { toCacheKey(accountId); }
Defensive patterns

Strategy: validation

Validate before calling

function hasNonEmptySegment(value: unknown): value is string {
  return typeof value === "string" && value.trim().length > 0;
}
// before caching
if (!hasNonEmptySegment(accountId)) { /* skip cache vend, or disable cache */ }

Type guard

function isNonEmptySafeSegment(value: unknown): value is string {
  if (typeof value !== "string") return false;
  const t = value.trim();
  return t.length > 0 && t !== "." && t !== ".." && !t.includes("/") && !t.includes("\\") && !t.includes("\0");
}

Try / catch

try {
  const key = toCacheKey(accountId);
} catch (e) {
  if (e instanceof Error && /codex auth cache: .* is empty/.test(e.message)) {
    // credential has no account_id; skip the cache path, do not crash the run
  } else throw e;
}

Prevention

When it happens

Trigger: toCacheKey(accountId) is called with an empty/whitespace accountId (e.g. the auth.json had no account_id), or resolveCodexAuthCacheDir(env, companyId) is called with an empty companyId. The label in the message identifies which value was empty ("account_id" or "companyId").

Common situations: A Codex auth.json is an API-key-only file with no tokens.account_id, so the cache write path receives an empty accountId; or a code path constructs the cache dir with a missing company id during a test/migration.

Related errors


AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12). Data as JSON: /api/errors/40cd11dea8b0b189. Report an issue: GitHub.