paperclipai/paperclip · error · Error

request.session.provider must match request.provider

Error message

request.session.provider must match request.provider

What it means

parseEvalSessionRequest requires that when request.session.provider is supplied it must exactly equal the top-level request.provider (eval-session-contract.ts:291-293). The session block is the CreateCapabilityLiveSessionInput forwarded to CapabilityLiveSessionService, and a mismatch would mean the live session runs on a different agent provider than the eval request declares. The guard is fail-closed: omit session.provider to inherit the top-level value, or keep the two identical.

Source

Thrown at packages/paperclip-runner/src/cli/eval-session-contract.ts:301

    throw new Error("request.runnerd.sha256 must be a SHA-256 digest");
  }
  const limits = object(input.limits, "request.limits");
  const sessionInput = object(input.session, "request.session");
  const session = sessionInput as unknown as CreateCapabilityLiveSessionInput;
  const model = text(input.model, "request.model");
  if (provider === "claude_managed" && model !== "claude-sonnet-5") {
    throw new Error("Claude Managed evals require exact model claude-sonnet-5");
  }
  if (
    provider === "aws_agentcore" &&
    model !== "global.anthropic.claude-sonnet-4-6"
  ) {
    throw new Error(
      "AWS AgentCore evals require exact model global.anthropic.claude-sonnet-4-6",
    );
  }
  if (sessionInput.provider !== undefined && sessionInput.provider !== provider) {
    throw new Error("request.session.provider must match request.provider");
  }
  if (
    session.requestedModel !== undefined &&
    session.requestedModel !== model
  ) {
    throw new Error("request.session.requestedModel must match request.model");
  }
  if (session.acpxAgent === "pi") {
    throw new Error("The Pi ACPX profile is not available");
  }

  return {
    schema: EVAL_SESSION_REQUEST_SCHEMA,
    attemptId: text(input.attemptId, "request.attemptId"),
    prompt: text(input.prompt, "request.prompt"),
    model,
    provider,
    driver,

View on GitHub (pinned to 01ad858492)

Solutions

  1. Set request.session.provider to the same value as request.provider, or delete request.session.provider entirely so it defaults to the top-level provider.
  2. Search the request JSON for "session" and verify every mirrored field (provider, requestedModel) matches the top-level request fields.
  3. Regenerate the request from the tooling that pairs provider and session together instead of editing by hand.
  4. If this happens programmatically, fix the code that builds CreateCapabilityLiveSessionInput to derive session.provider from the same variable used for request.provider.

Example fix

// before
{
  "provider": "codex",
  "session": { "provider": "opencode", ... }
}
// after
{
  "provider": "codex",
  "session": { "provider": "codex", ... }
}
// or simply remove session.provider to inherit it
Defensive patterns

Strategy: validation

Validate before calling

function assertSessionProviderMatches(req: { provider?: string; session?: { provider?: string } }): void {
  if (req.session?.provider !== undefined && req.session.provider !== req.provider) {
    throw new Error(`session.provider (${req.session.provider}) != provider (${req.provider})`);
  }
}

Type guard

function hasConsistentProvider(req: { provider?: string; session?: { provider?: string } }): boolean {
  return req.session?.provider === undefined || req.session.provider === req.provider;
}

Try / catch

try {
  const parsed = parseEvalSessionRequest(rawRequest);
  // proceed
} catch (err) {
  if (err instanceof Error && err.message === "request.session.provider must match request.provider") {
    // normalize: delete session.provider or set it from top-level provider, then retry
  } else throw err;
}

Prevention

When it happens

Trigger: Submitting an eval-session request where request.provider is e.g. 'codex' but request.session.provider is 'opencode', 'claude_managed', 'acpx', or any other string (including undefined-vs-typo differences like 'claude_managed' vs 'claude-managed').

Common situations: Hand-editing one field of the request JSON and not the nested session block; copying a session object from a request built for a different provider; template/stale request files after a provider migration.

Related errors


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