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

  1. Raise the timeout argument passed to waitForRun for workloads known to run long.
  2. Move the user-facing flow to async/job-based completion so the HTTP request is not blocked by polling.
  3. Check the run status upstream (retrieveRun) after the timeout to confirm whether it eventually completed, and reconcile.
  4. 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

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

Related errors


AI-assisted analysis of danny-avila/LibreChat@5ff282f900 (2026-08-12). Data as JSON: /api/errors/900ce54f7975376c. Report an issue: GitHub.