paperclipai/paperclip · error
codex auth cache: account_id is not a valid account handle
Error message
codex auth cache: account_id is not a valid account handle
What it means
resolveCodexAuthCacheEntryPath() builds the on-disk cache entry path from the account id. The id is first converted to a safe handle via toAccountHandle(); if that fails there is no safe directory key to use, so the function throws rather than constructing an unsafe path. This protects both path traversal safety and the handle-to-directory correspondence.
Source
Thrown at packages/adapters/codex-local/src/server/codex-auth-cache.ts:132
/**
* Resolves the entry path for one identity: `<cacheRoot>/<safeAccountId>/auth.json`.
* The `account_id` is validated first by {@link toAccountHandle} (a strict
* allowlist), the entry point of this function, then sanitized again by
* {@link toCacheKey} (a denylist) as a second, independent layer. After the
* join, this verifies the resolved entry path stays under the cache root and
* ends at exactly `<safeAccountId>/auth.json`. This function does no filesystem
* work; it is safe for a read path (the vend and the clear). (Security
* condition 3.)
*/
export function resolveCodexAuthCacheEntryPath(
env: NodeJS.ProcessEnv = process.env,
accountId: string,
companyId: string,
): string {
const handle = toAccountHandle(accountId);
if (!handle) {
throw new Error("codex auth cache: account_id is not a valid account handle");
}
const resolvedRoot = resolveCodexAuthCacheDir(env, companyId);
const safeKey = toCacheKey(handle);
const entryDir = path.resolve(resolvedRoot, safeKey);
const entryPath = path.resolve(entryDir, CACHE_ENTRY_FILE);
const expectedEntryPath = path.join(resolvedRoot, safeKey, CACHE_ENTRY_FILE);
if (
!entryDir.startsWith(resolvedRoot + path.sep) ||
path.dirname(entryDir) !== resolvedRoot ||
entryPath !== expectedEntryPath
) {
throw new Error("codex auth cache: resolved entry path escapes the cache root");
}
return entryPath;
}
/**
* Ensures one directory exists and is private (mode 0700). Fails closed withView on GitHub (pinned to 01ad858492)
Solutions
- Verify the accountId passed in is the raw Codex account_id from the auth payload, not a label or email.
- Check that the auth source actually contains a non-empty account_id; re-login to Codex if it is missing.
- Normalize/strip unsafe characters from the id before calling, or skip the cache lookup when no valid id exists.
- Compare against toAccountHandle() in codex-auth-cache.ts to confirm which characters are accepted.
Example fix
// before
const entryPath = resolveCodexAuthCacheEntryPath(env, auth.accountId ?? "", companyId);
// after
if (!auth.accountId || toAccountHandle(auth.accountId) === null) {
return null; // no valid cached entry possible
}
const entryPath = resolveCodexAuthCacheEntryPath(env, auth.accountId, companyId); Defensive patterns
Strategy: validation
Validate before calling
import { toAccountHandle } from "./account-handle";
function hasCacheableAccountId(accountId: string | undefined): boolean {
return accountId !== undefined && toAccountHandle(accountId) !== null;
} Type guard
function isCacheableAccountId(v: unknown): v is string {
return typeof v === "string" && v.length > 0 && toAccountHandle(v) !== null;
} Try / catch
try {
const entry = resolveCodexAuthCacheEntryPath(env, accountId, companyId);
return readAuthCacheEntry(entry);
} catch (err) {
if (err instanceof Error && err.message.includes("not a valid account handle")) {
return null; // no cacheable identity; fall through to fresh login
}
throw err;
} Prevention
- Guard every auth-cache lookup with a toAccountHandle() check before calling.
- Never pass emails, display names, or trimmed ids into the cache API — use the raw account_id.
- Treat a missing/invalid account_id as 'no cached auth' rather than an error condition in callers.
- Log the offending raw value when this occurs to spot upstream format changes early.
When it happens
Trigger: Calling any of the auth-cache accessors (accountAuth, entryPath, resolveEntry, execute, prepareCodexHelloProbe) with an accountId that toAccountHandle() rejects — empty string, only unsafe characters, or ids containing '/' or '..' segments.
Common situations: A stale or corrupted Codex auth.json lacking a usable account id; passing a display name or email instead of the raw account id; upstream Codex changing id format; reading auth state before first login completes.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- Codex working directory must exist before provider admission
- ${label} is not a regular file at ${canonical}.
- Invalid GitHub launcher run ID
- workspace_durable_seed_invalid
- device-login promotion: the account identifier cannot form a
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/a79fe67fe0e09b9f.
Report an issue: GitHub.