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

  1. Edit config.toml so the entry for the repo root is a table: [projects."<abs-root>"] with trust_level inside it.
  2. Delete the malformed projects key and let the driver recreate it as trusted.
  3. Verify the resolved root (realpath of cwd) matches the key you edited — symlinks can change which entry is read.
  4. 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

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


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/2a4fc23ea65a274a. Report an issue: GitHub.