different-ai/openwork · error · Error

CUA API error ${response.status}: ${errorText.slice(0, 300)}

Error message

CUA API error ${response.status}: ${errorText.slice(0, 300)}

What it means

After POSTing the conversation items to the OpenAI computer-use endpoint, runCuaLoop checks response.ok. Any non-2xx (401 invalid key, 402 quota, 429 rate limit, 400 bad model/input, 5xx) becomes this error with the HTTP status and up to 300 chars of the response body, so the actual API error reason is embedded in the message.

Source

Thrown at packages/handsfree/src/cua-runner.mjs:36

  onProgress?.({ kind: "start", width: displayInfo.width, height: displayInfo.height });

  const items = [{ role: "user", content: String(task ?? "") }];
  const messages = [];

  for (let turn = 0; turn < maxTurns; turn += 1) {
    if (signal?.aborted) return { ok: true, messages, turns: turn, aborted: true };
    onProgress?.({ kind: "turn", turn: turn + 1 });

    const response = await fetch("https://api.openai.com/v1/responses", {
      method: "POST",
      headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
      body: JSON.stringify({ model, input: items, tools: [{ type: "computer" }] }),
      signal,
    });

    if (!response.ok) {
      const errorText = await response.text().catch(() => "");
      throw new Error(`CUA API error ${response.status}: ${errorText.slice(0, 300)}`);
    }

    const result = await response.json();
    const output = result.output || [];
    if (!output.length) throw new Error("No output from CUA model.");
    items.push(...output);

    let computerCall = null;
    for (const item of output) {
      if (item.type === "message") {
        const text = item.content?.map((part) => part.text || "").join("") || "";
        if (text) {
          messages.push(text);
          onProgress?.({ kind: "message", text });
        }
      }
      if (item.type === "computer_call") computerCall = item;
    }

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Read the embedded errorText in the message — it contains OpenAI's JSON error explaining the real cause (e.g. 'Incorrect API key', 'insufficient_quota').
  2. For 401/402: fix the API key or billing at platform.openai.com; for 429: add backoff/retry with exponential delay.
  3. For 400: confirm `model` supports the computer-use tool and that pushed items are valid Responses-API output items.
  4. Wrap the runCuaLoop call in retry logic for 429/5xx statuses before surfacing to the user.
Defensive patterns

Strategy: retry

Try / catch

try {
  await runCuaLoop(opts);
} catch (e) {
  const m = /CUA API error (\d+)/.exec(e.message);
  const status = m ? Number(m[1]) : 0;
  if (status === 429 || status >= 500) await backoffRetry(opts);
  else throw e; // 401/402/400 are not retryable
}

Prevention

When it happens

Trigger: Any failed fetch from the loop's model request — invalid/revoked API key (401), out-of-credit org (402), rate limiting (429), unknown `model` name, malformed `items` pushed into the conversation (400), or transient 500/503 from OpenAI.

Common situations: Expired or rotated OpenAI keys; free-tier org with no computer-use access; sending stale tool outputs or oversized screenshots producing a 400; regional outages returning 5xx; corporate proxy stripping the Authorization header.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


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