paperclipai/paperclip · error
provider turn ended with status ${turn.status}
Error message
provider turn ended with status ${turn.status} What it means
After sending the eval prompt, the session's turn result must have status "completed". Any other terminal status (timeout, failed, cancelled, etc.) causes the runner to mark the attempt failed with reason 'provider_turn_<status>' and throw this error. The run then falls into the catch path which writes a failure artifact and exits with code 2.
Source
Thrown at packages/paperclip-runner/src/cli/eval-session.ts:201
provider: requestedProvider,
requestedModel: request.model,
...(requestedProvider === "acpx"
? { acpxAgent: request.acpxAgent ?? "codex" }
: { acpxAgent: undefined }),
...(request.managedProfile === undefined
? {}
: { managedProfile: request.managedProfile }),
...(request.agentCoreProfile === undefined
? {}
: { agentCoreProfile: request.agentCoreProfile }),
attemptId: request.attemptId,
turnTimeoutMs: request.limits.turnTimeoutMs,
} as unknown as CreateCapabilityLiveSessionInput;
session = await service.create(createInput);
turn = await session.sendMessage(request.prompt);
if (turn.status !== "completed") {
await session.completeAttempt("failed", `provider_turn_${turn.status}`);
throw new Error(`provider turn ended with status ${turn.status}`);
}
const usage = evalSessionUsage(request.model, turn.snapshot);
if (usage.agentTurns > request.limits.maxAgentTurns) {
throw new Error("agent turn limit exceeded");
}
if (
usage.estimatedCostNanodollars >
request.limits.maxEstimatedCostNanodollars
) {
throw new Error("estimated cost limit exceeded");
}
if (
usage.providerReportedCostNanodollars >
request.limits.maxEstimatedCostNanodollars
) {
throw new Error("provider-reported cost limit exceeded");
}
await session.completeAttempt("succeeded");View on GitHub (pinned to 5716fe907e)
Solutions
- Inspect the written output artifact (cli.outputPath) — infrastructureFailure classifies retryability and the snapshot/turn fields show what the provider actually did
- If failureClass marked it retryable (e.g. 'provider_turn_timeout'), rerun the eval session — timeouts are transient
- Increase request.limits.turnTimeoutMs if the provider legitimately needs longer per turn
- Verify the provider binary/version is correctly installed and authenticated (e.g. opencode pinned to 1.18.17 per providerVersion)
- Check turn/diagnostics in the artifact for provider stderr to fix the underlying provider failure
Example fix
// before (request JSON, tight timeout)
"limits": { "turnTimeoutMs": 30000, ... }
// after
"limits": { "turnTimeoutMs": 300000, ... }
// ...then rerun; for transient provider failures simply retry the session. Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight provider sanity check before running the eval session
async function assertProviderReady(provider: string, version: string | null): Promise<void> {
// e.g. verify binary on PATH and correct pinned version
const { execFile } = await import("node:child_process");
const { promisify } = await import("node:util");
const run = promisify(execFile);
await run(provider, ["--version"]); // throws early if provider missing/broken
} Type guard
interface TurnLike { status: string }
function isCompletedTurn(turn: TurnLike): turn is TurnLike & { status: "completed" } {
return turn.status === "completed";
} Try / catch
try {
const code = await runEvalSessionCli(args);
process.exitCode = code;
} catch (error) {
if (error instanceof Error && error.message.startsWith("provider turn ended with status")) {
// Failure artifact was already written to --output; read infrastructureFailure.retryable
const artifact = JSON.parse(await readFile(outputPath, "utf8"));
if (artifact.infrastructureFailure?.retryable) {
// transient (e.g. timeout): retry once with backoff
process.exitCode = await runEvalSessionCli(args);
} else {
console.error(`Non-retryable provider failure: ${artifact.infrastructureError}`);
process.exitCode = 2;
}
return;
}
throw error;
} Prevention
- Set request.limits.turnTimeoutMs generously enough for real provider latency
- Check the failure artifact's infrastructureFailure.class: provider_turn_timeout is retryable, other statuses usually are not
- Pre-verify provider auth and pinned binary version (opencode must be 1.18.17) before launching eval sessions
- Retry transient provider failures with bounded backoff instead of failing the whole eval run
- Inspect the turn/snapshot fields in the output artifact for provider stderr before changing code
When it happens
Trigger: session.sendMessage resolves with turn.status !== "completed" — provider process timed out (turnTimeoutMs exceeded), provider crashed or exited early, or the provider reported a non-success turn outcome.
Common situations: Provider binary hangs or is slow and the turn timeout elapses; provider CLI version/auth broken so the turn errors out; flaky provider infrastructure during eval runs; prompt too large causing provider-side failure.
Related errors
- runner_goal_stop_unconfirmed
- Failed to stop Daytona sandbox during lease release: ${forma
- Plugin UI is not available (status: ${plugin.status})
- [opencode-local] Remote model availability probe for "${mode
- Plugin is not ready (current status: ${plugin.status})
AI-assisted analysis of paperclipai/paperclip@5716fe907e (2026-09-02).
Data as JSON: /api/errors/56c299b921e56fb8.
Report an issue: GitHub.