different-ai/openwork · warning · ApiError

agent_diagnostics_busy

agent_diagnostics_busy

Error message

Agent diagnostics are temporarily busy

What it means

This 429 error is thrown by reserveAgentDiagnosticsRun when the number of concurrent agent diagnostics runs on a single server config has reached AGENT_DIAGNOSTICS_MAX_IN_FLIGHT_PER_SERVER. Unlike error 330 (same key already reserved), this fires for any new key once the pool is full. It is a temporary capacity backpressure signal.

Source

Thrown at apps/server/src/server.ts:217

  agentDiagnosticsLastRun.set(key, now);
}

function reserveAgentDiagnosticsRun(
  config: ServerConfig,
  actor: Actor | undefined,
  workspaceId: string,
): () => void {
  const key = agentDiagnosticsActorWorkspaceKey(actor, workspaceId);
  const inFlight = agentDiagnosticsInFlightByServer.get(config) ?? new Set<string>();
  agentDiagnosticsInFlightByServer.set(config, inFlight);
  // Preserve the existing cooldown response for ordinary repeated attempts.
  // A zero/expired cooldown still cannot bypass the in-flight reservation.
  requireAgentDiagnosticsRateLimit(config, actor, workspaceId);
  if (inFlight.has(key)) {
    throw new ApiError(429, "agent_diagnostics_in_progress", "Agent diagnostics are already in progress");
  }
  if (inFlight.size >= AGENT_DIAGNOSTICS_MAX_IN_FLIGHT_PER_SERVER) {
    throw new ApiError(429, "agent_diagnostics_busy", "Agent diagnostics are temporarily busy");
  }

  // The cooldown charge and reservation are synchronous, so no second request
  // for this actor/workspace can slip in between them.
  inFlight.add(key);
  let released = false;
  return () => {
    if (released) return;
    released = true;
    inFlight.delete(key);
  };
}

const OPENWORK_VOICE_REALTIME_TOOLS = [
  {
    type: "function",
    name: "openwork_snapshot",
    description: "Read the current OpenWork UI control snapshot: route, status, narration, and visible action metadata.",

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Reduce concurrency of diagnostics calls client-side (semaphore/queue)
  2. Retry after a delay using exponential backoff until a slot frees
  3. Stagger diagnostics runs across workspaces
  4. Raise AGENT_DIAGNOSTICS_MAX_IN_FLIGHT_PER_SERVER only if server capacity allows

Example fix

// before
await Promise.all(workspaces.map(w => runDiagnostics(w.id)));
// after
for (const w of workspaces) {
  await withConcurrencyLimit(2, () => runDiagnostics(w.id));
}
Defensive patterns

Strategy: retry

Validate before calling

// cap client concurrency below the server limit
const MAX_CONCURRENT = 2; // < AGENT_DIAGNOSTICS_MAX_IN_FLIGHT_PER_SERVER
if (outstandingDiagnostics >= MAX_CONCURRENT) await queue.wait();

Try / catch

try {
  await reserveAgentDiagnosticsRun(config);
} catch (e) {
  if (e.code === "agent_diagnostics_busy") {
    await sleep(backoffMs);
    return reserveWithRetry(config); // exponential backoff
  }
  throw e;
}

Prevention

When it happens

Trigger: POSTing a new agent diagnostics request (unique actor/workspace key) when inFlight.size >= AGENT_DIAGNOSTICS_MAX_IN_FLIGHT_PER_SERVER for that server config. Multiple distinct workspaces or actors starting diagnostics simultaneously.

Common situations: CI pipelines running diagnostics for many workspaces in parallel against one server; org-wide 'diagnose everything' scripts; bursts after a mass config change.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/acfd481bb4f694c1. Report an issue: GitHub.