paperclipai/paperclip · error
codex_startup_trust_invalid_project
codex_startup_trust_invalid_project
Error message
codex_startup_trust_invalid_project
What it means
After validating the projects map, the function reads the entry for the resolved trust root and requires it to be a plain object so it can spread and add trust_level. It throws this error when projects[root] is a scalar, array, or Date instead of a table.
Source
Thrown at packages/paperclip-runner/src/drivers/codex/codex-startup-trust.ts:108
}
mkdirSync(codexHome, { recursive: true, mode: 0o700 });
const path = join(codexHome, "config.toml");
const source = existsSync(path) ? readFileSync(path, "utf8") : "";
const config = parse(source);
const projects = config.projects ?? {};
if (
typeof projects !== "object" ||
Array.isArray(projects) ||
projects instanceof Date
)
throw new Error("codex_startup_trust_invalid_projects");
const project = projects[root] ?? {};
if (
typeof project !== "object" ||
Array.isArray(project) ||
project instanceof Date
)
throw new Error("codex_startup_trust_invalid_project");
config.projects = {
...projects,
[root]: { ...project, trust_level: "trusted" },
};
const updated = editTrust(source, root, config);
if (updated === source) return;
const temporary = resolve(codexHome, `config.toml.${randomUUID()}.tmp`);
try {
writeFileSync(temporary, updated, { mode: 0o600, flag: "wx" });
renameSync(temporary, path);
} finally {
rmSync(temporary, { force: true });
}
}
View on GitHub (pinned to 01ad858492)
Solutions
- Edit config.toml so the entry for the repo root is a table: [projects."<abs-root>"] with trust_level inside it.
- Delete the malformed projects key and let the driver recreate it as trusted.
- Verify the resolved root (realpath of cwd) matches the key you edited — symlinks can change which entry is read.
- Regenerate a clean config.toml from scratch if multiple entries are malformed.
Example fix
// before (config.toml) [projects] "/repo" = true // after (config.toml) [projects."/repo"] trust_level = "trusted"
Defensive patterns
Strategy: type-guard
Validate before calling
const entry = readParsedConfig().projects?.[realpathSync(cwd)];
if (entry !== undefined && !(typeof entry === 'object' && entry !== null && !Array.isArray(entry) && !(entry instanceof Date))) throw new Error(`projects["${cwd}"] must be a table, got ${typeof entry}`); Type guard
const isProjectTable = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null && !Array.isArray(v) && !(v instanceof Date);
Try / catch
try { trustCodexStartupRoot(codexHome, cwd); } catch (e) { if ((e as Error).message === 'codex_startup_trust_invalid_project') { rewriteProjectEntryAsTable(root); trustCodexStartupRoot(codexHome, cwd); } else throw e; } Prevention
- Write trust entries as [projects."<abs-root>"] tables with trust_level inside
- Check for symlink/realpath differences between the key you set and the resolved root
- Lint ~/.codex/config.toml after manual edits or Codex CLI upgrades
- One machine-writer per config file to avoid competing formats
When it happens
Trigger: config.toml contains an entry like projects."/repo" = true (or a string/number/array) instead of a [projects."/repo"] table, for the exact resolved repo root path.
Common situations: Manual trust edits writing a boolean flag, older Codex formats storing project settings as scalars, path mismatches after symlinks changing the root key so an unexpected entry is read.
Related errors
- codex_startup_trust_invalid_projects
- ACPX provider identity contains an invalid permission mode
- ACPX provider identity contains invalid lifetime fences
- codex_startup_trust_cannot_preserve_configuration
- codex_startup_trust_requires_absolute_paths
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/2a4fc23ea65a274a.
Report an issue: GitHub.