{"record":{"id":"56c299b921e56fb8","repo":"paperclipai/paperclip","slug":"provider-turn-ended-with-status-turn-status","errorCode":null,"errorMessage":"provider turn ended with status ${turn.status}","messagePattern":"provider turn ended with status (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/cli/eval-session.ts","lineNumber":201,"sourceCode":"      provider: requestedProvider,\n      requestedModel: request.model,\n      ...(requestedProvider === \"acpx\"\n        ? { acpxAgent: request.acpxAgent ?? \"codex\" }\n        : { acpxAgent: undefined }),\n      ...(request.managedProfile === undefined\n        ? {}\n        : { managedProfile: request.managedProfile }),\n      ...(request.agentCoreProfile === undefined\n        ? {}\n        : { agentCoreProfile: request.agentCoreProfile }),\n      attemptId: request.attemptId,\n      turnTimeoutMs: request.limits.turnTimeoutMs,\n    } as unknown as CreateCapabilityLiveSessionInput;\n    session = await service.create(createInput);\n    turn = await session.sendMessage(request.prompt);\n    if (turn.status !== \"completed\") {\n      await session.completeAttempt(\"failed\", `provider_turn_${turn.status}`);\n      throw new Error(`provider turn ended with status ${turn.status}`);\n    }\n    const usage = evalSessionUsage(request.model, turn.snapshot);\n    if (usage.agentTurns > request.limits.maxAgentTurns) {\n      throw new Error(\"agent turn limit exceeded\");\n    }\n    if (\n      usage.estimatedCostNanodollars >\n      request.limits.maxEstimatedCostNanodollars\n    ) {\n      throw new Error(\"estimated cost limit exceeded\");\n    }\n    if (\n      usage.providerReportedCostNanodollars >\n      request.limits.maxEstimatedCostNanodollars\n    ) {\n      throw new Error(\"provider-reported cost limit exceeded\");\n    }\n    await session.completeAttempt(\"succeeded\");","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/paperclipai/paperclip/blob/5716fe907e596ce73501408fc6efdb19fb61edf2/packages/paperclip-runner/src/cli/eval-session.ts#L183-L219","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before (request JSON, tight timeout)\n\"limits\": { \"turnTimeoutMs\": 30000, ... }\n// after\n\"limits\": { \"turnTimeoutMs\": 300000, ... }\n// ...then rerun; for transient provider failures simply retry the session.","handlingStrategy":"try-catch","validationCode":"// Pre-flight provider sanity check before running the eval session\nasync function assertProviderReady(provider: string, version: string | null): Promise<void> {\n  // e.g. verify binary on PATH and correct pinned version\n  const { execFile } = await import(\"node:child_process\");\n  const { promisify } = await import(\"node:util\");\n  const run = promisify(execFile);\n  await run(provider, [\"--version\"]); // throws early if provider missing/broken\n}","typeGuard":"interface TurnLike { status: string }\nfunction isCompletedTurn(turn: TurnLike): turn is TurnLike & { status: \"completed\" } {\n  return turn.status === \"completed\";\n}","tryCatchPattern":"try {\n  const code = await runEvalSessionCli(args);\n  process.exitCode = code;\n} catch (error) {\n  if (error instanceof Error && error.message.startsWith(\"provider turn ended with status\")) {\n    // Failure artifact was already written to --output; read infrastructureFailure.retryable\n    const artifact = JSON.parse(await readFile(outputPath, \"utf8\"));\n    if (artifact.infrastructureFailure?.retryable) {\n      // transient (e.g. timeout): retry once with backoff\n      process.exitCode = await runEvalSessionCli(args);\n    } else {\n      console.error(`Non-retryable provider failure: ${artifact.infrastructureError}`);\n      process.exitCode = 2;\n    }\n    return;\n  }\n  throw error;\n}","preventionTips":["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"],"tags":["provider","eval","timeout","lifecycle"],"backgroundTag":"provider-turn-failed","analyzedSha":"5716fe907e596ce73501408fc6efdb19fb61edf2","analyzedAt":"2026-09-02T18:44:00.616Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}