paperclipai/paperclip · error · Error
No credential policy for ${config.provider ?? "codex"}/${con
Error message
No credential policy for ${config.provider ?? "codex"}/${config.acpxAgent ?? "default"} What it means
credentialForConfig maps an eval-case config (provider + acpxAgent) to the environment variable holding the required API credential. If the combination is not covered by the hardcoded policy (opencode→OPENROUTER_API_KEY; acpx pi→OPENROUTER, claude→ANTHROPIC, codex→OPENAI; codex provider default), it throws naming the provider/agent pair. Every live eval lane must have a known credential source.
Source
Thrown at packages/paperclip-runner/scripts/runner-protocol-eval-campaign.mjs:69
if (!rel || rel === ".." || rel.startsWith(`..${sep}`)) {
throw new Error(`${label} escapes its declared root`);
}
return resolve(candidate);
}
export function credentialForConfig(config) {
if (config.provider === "opencode") return "OPENROUTER_API_KEY";
if (config.provider === "claude_managed") return "ANTHROPIC_API_KEY";
if (config.provider === "aws_agentcore") return "AWS_AGENTCORE_OIDC";
if (config.provider === "codex" || config.provider === undefined) {
return "OPENAI_API_KEY";
}
if (config.provider === "acpx") {
if (config.acpxAgent === "pi") return "OPENROUTER_API_KEY";
if (config.acpxAgent === "claude") return "ANTHROPIC_API_KEY";
if (config.acpxAgent === "codex") return "OPENAI_API_KEY";
}
throw new Error(
`No credential policy for ${config.provider ?? "codex"}/${config.acpxAgent ?? "default"}`,
);
}
function parseRosterSelection(value) {
if (!value?.trim() || value.trim() === "all") return null;
const selected = value
.split(",")
.map((entry) => entry.trim())
.filter(Boolean);
if (selected.length === 0 || new Set(selected).size !== selected.length) {
throw new Error("Roster selection must contain unique comma-separated IDs");
}
return new Set(selected);
}
async function maintainedRosterSelection(programRoot) {
const campaignPath = resolve(programRoot, "campaigns/live-direct-full.json");View on GitHub (pinned to 01ad858492)
Solutions
- Fix the provider/acpxAgent values in the roster case config to a supported combination.
- Add a mapping line to credentialForConfig for the new provider or acpx agent and set the corresponding env var.
- Export the required credential env var (OPENROUTER_API_KEY, ANTHROPIC_API_KEY, or OPENAI_API_KEY) — note this error names the policy gap, not the missing env var itself.
Example fix
// before if (config.acpxAgent === "codex") return "OPENAI_API_KEY"; // after if (config.acpxAgent === "codex") return "OPENAI_API_KEY"; if (config.acpxAgent === "gemini") return "GEMINI_API_KEY";
Defensive patterns
Strategy: validation
Validate before calling
const SUPPORTED = { opencode: true, codex: true, acpx: ["pi", "claude", "codex"] };
const ok = config.provider === "opencode" || config.provider === "codex" || (config.provider === "acpx" && SUPPORTED.acpx.includes(config.acpxAgent));
if (!ok) throw new Error(`Unsupported provider/agent: ${config.provider}/${config.acpxAgent}`); Type guard
const hasCredentialPolicy = (c) => c.provider === "opencode" || c.provider === "codex" || (c.provider === "acpx" && ["pi", "claude", "codex"].includes(c.acpxAgent));
Try / catch
try {
const envVar = credentialForConfig(config);
if (!process.env[envVar]) throw new Error(`${envVar} is not set`);
} catch (err) {
if (err.message.startsWith("No credential policy")) console.error("Fix provider/acpxAgent in the roster case or extend credentialForConfig.");
throw err;
} Prevention
- Keep the set of provider/acpxAgent values in rosters in sync with credentialForConfig.
- When adding a new agent, add its credential mapping and required env var in the same PR.
- Dry-run catalog building before a live run to catch policy gaps early.
When it happens
Trigger: Adding an eval case with an unknown provider value, or an acpx agent other than pi/claude/codex, or a config missing both provider and acpxAgent that doesn't fall through to the codex default covered by the policy.
Common situations: Typo'd provider name (e.g. 'opnrouter'); a newly supported acpx agent added to rosters before updating the credential policy; a case config with provider: 'acpx' but acpxAgent misspelled or unset when policy requires one of the three.
Related errors
- Unknown config key ${warning.path}; did you mean ${warning.s
- Unknown config key ${warning.path}; did you mean ${warning.s
- "configJson" is required and must be an object
- Configuration does not match the plugin's instanceConfigSche
- ${prefix}: "captureCredential" must be a function when prese
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/6fd9af6ffe8ca9ea.
Report an issue: GitHub.