{"record":{"id":"ec0d25356630ca3f","repo":"danny-avila/LibreChat","slug":"unexpected-run-status-run-status-nfull-run-inf","errorCode":null,"errorMessage":"Unexpected run status ${run.status}.\\nFull run info:\\n\\n${runInfo}","messagePattern":"Unexpected run status (.+?)\\.\\\\nFull run info:\\\\n\\\\n(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"api/server/services/AssistantService.js","lineNumber":108,"sourceCode":"  } else if (run.status === RunStatus.REQUIRES_ACTION) {\n    const actions = [];\n    run.required_action?.submit_tool_outputs.tool_calls.forEach((item) => {\n      const functionCall = item.function;\n      const args = JSON.parse(functionCall.arguments);\n      actions.push({\n        tool: functionCall.name,\n        toolInput: args,\n        toolCallId: item.id,\n        run_id,\n        thread_id,\n      });\n    });\n\n    return actions;\n  }\n\n  const runInfo = JSON.stringify(run, null, 2);\n  throw new Error(`Unexpected run status ${run.status}.\\nFull run info:\\n\\n${runInfo}`);\n}\n\n/**\n * Filters the steps to keep only the most recent instance of each unique step.\n * @param {RunStep[]} steps - The array of RunSteps to filter.\n * @return {RunStep[]} The filtered array of RunSteps.\n */\nfunction filterSteps(steps = []) {\n  if (steps.length <= 1) {\n    return steps;\n  }\n  const stepMap = new Map();\n\n  steps.forEach((step) => {\n    if (!step) {\n      return;\n    }\n","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/danny-avila/LibreChat/blob/5ff282f9006c436e561de1afd39a481bea1ef0d8/api/server/services/AssistantService.js#L90-L126","documentation":"Thrown when an Assistants API run object has a status the code does not recognize as a terminal or actionable state (e.g. not completed, requires_action, failed, cancelled, expired, in_progress). The full run JSON is appended so the unhandled status can be diagnosed.","triggerScenarios":"OpenAI introduced a new run status value not yet handled by this version of the code; a polling/processing path received a status like 'cancelling' or a future status mid-flight.","commonSituations":"API version drift — the provider adds a status after this code shipped; a race where a run transitions to an intermediate state the loop did not expect.","solutions":["Upgrade to a version of the code that handles the new status returned in runInfo.","If the status is transient, retry the run fetch/poll after a short delay.","Inspect runInfo in the logs to identify the exact unhandled status and add a handler.","Pin the Assistants API version to one whose status set is fully handled."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const KNOWN_STATUSES = new Set(['queued','in_progress','requires_action','completed','failed','cancelled','expired','cancelling']);\nif (!KNOWN_STATUSES.has(run.status)) {\n  throw new Error(`Unhandled run status: ${run.status}`);\n}","typeGuard":"function isKnownRunStatus(status) {\n  return ['queued','in_progress','requires_action','completed','failed','cancelled','expired','cancelling'].includes(status);\n}","tryCatchPattern":"try { await processRun(run); }\ncatch (e) {\n  if (/Unexpected run status/.test(e.message)) { logger.warn(e.message); await sleep(1000); return pollRun(run.id); }\n  throw e;\n}","preventionTips":["Keep the Assistants client version aligned with the API version you call.","Add a default branch that logs and re-polls unknown statuses.","Pin the API version in production."],"tags":["assistants","run-status","api-drift","openai"],"backgroundTag":null,"analyzedSha":"5ff282f9006c436e561de1afd39a481bea1ef0d8","analyzedAt":"2026-08-12T21:38:08.145Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}