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

  1. Fix the provider/acpxAgent values in the roster case config to a supported combination.
  2. Add a mapping line to credentialForConfig for the new provider or acpx agent and set the corresponding env var.
  3. 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

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


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