danny-avila/LibreChat · error · Error
[waitForRun] ${runInfo} | status: ${run.status} | timed out
Error message
[waitForRun] ${runInfo} | status: ${run.status} | timed out after ${timeout} ms What it means
Thrown by waitForRun when the outer polling loop accumulates timeElapsed >= timeout (default 60000*3 = 180000 ms) and the run has not left the IN_PROGRESS/QUEUED states. The run is still active upstream but the client gave up waiting; the last-known run status is included in the message.
Source
Thrown at api/server/services/Runs/handle.js:165
break;
}
// may use in future; for now, just fetch from the final status
await runManager.fetchRunSteps({
openai,
thread_id: thread_id,
run_id: run_id,
runStatus: run.status,
});
await sleep(pollIntervalMs);
timeElapsed += pollIntervalMs;
}
if (timeElapsed >= timeout) {
const timeoutMessage = `[waitForRun] ${runInfo} | status: ${run.status} | timed out after ${timeout} ms`;
logger.warn(timeoutMessage);
throw new Error(timeoutMessage);
}
return run;
}
/**
* Retrieves all steps of a run.
*
* @deprecated: Steps are handled with runAssistant now.
* @param {Object} params - The parameters for the retrieveRunSteps function.
* @param {OpenAIClient} params.openai - The OpenAI client instance.
* @param {string} params.thread_id - The ID of the thread associated with the run.
* @param {string} params.run_id - The ID of the run to retrieve steps for.
* @return {Promise<RunStep[]>} A promise that resolves to an array of RunStep objects.
*/
async function _retrieveRunSteps({ openai, thread_id, run_id }) {
const runSteps = await openai.beta.threads.runs.steps.list(run_id, { thread_id });
return runSteps;View on GitHub (pinned to 5ff282f900)
Solutions
- Raise the timeout argument passed to waitForRun for workloads known to run long.
- Move the user-facing flow to async/job-based completion so the HTTP request is not blocked by polling.
- Check the run status upstream (retrieveRun) after the timeout to confirm whether it eventually completed, and reconcile.
- Reduce pollIntervalMs only if it helps detect terminal state sooner without increasing API load.
Example fix
// before
await waitForRun({ openai, run_id, thread_id, runManager }); // timeout defaults to 180000
// after
await waitForRun({ openai, run_id, thread_id, runManager, timeout: 10 * 60 * 1000 }); Defensive patterns
Strategy: retry
Validate before calling
const timeout = isLongRunningAgent ? 10 * 60 * 1000 : 3 * 60 * 1000;
await waitForRun({ openai, run_id, thread_id, runManager, timeout }); Try / catch
try {
await waitForRun({ openai, run_id, thread_id, runManager, timeout });
} catch (err) {
if (err.message.includes('timed out after')) {
// reconcile: poll retrieveRun once more to see if it finished
return { status: 'timeout', run_id, thread_id };
}
throw err;
} Prevention
- Pass an explicit timeout sized to the workload.
- Move long-running runs to an async/job model so polling does not block callers.
- After a timeout, reconcile upstream to capture a late completion.
When it happens
Trigger: A long-running assistant run (complex tool use, slow model, heavy file search) exceeding 3 minutes; a run stuck QUEUED due to OpenAI capacity; pollIntervalMs so large the budget is consumed before a state change can be observed.
Common situations: Code interpreter or function-calling runs that legitimately take minutes; busy periods on OpenAI; a tight default timeout for demanding workloads.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- [waitForRun] ${runIdLog} | Run retrieval failed after ${maxR
- [${req.baseUrl}] Invalid version: ${version}
- Unexpected run status ${run.status}.\nFull run info:\n\n${ru
- no_user_key
- Assistants API key not provided. Please provide it again.
AI-assisted analysis of danny-avila/LibreChat@5ff282f900 (2026-08-12).
Data as JSON: /api/errors/900ce54f7975376c.
Report an issue: GitHub.