nexu-io/open-design · error · Error

openai

Error message

openai ${resp.status}: ${await resp.text().catch(() => '')}

What it means

The OpenAI Chat Completions API returned a non-2xx HTTP status during the memory-LLM extraction call. The error includes the status code and raw response body. This is the callOpenAI path in the daemon's automatic memory extractor.

Solutions

  1. Check the API key is valid and has quota
  2. Verify provider.model is a model available on the account
  3. Confirm provider.baseUrl points to the correct OpenAI-compatible endpoint with /chat/completions path
  4. Inspect the response body for the specific error code
  5. Retry after a delay if rate-limited (429)
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const text = await callOpenAI(provider, system, user);
} catch (err) {
  if (err.message.startsWith('openai ')) {
    const status = parseInt(err.message.split(' ')[1], 10);
    if (status === 429) {
      await sleep(backoffMs);
      return callOpenAI(provider, system, user);
    }
    if (status === 401 || status === 403) {
      throw new Error('OpenAI API key is invalid or lacks permission');
    }
  }
  throw err;
}

Prevention

When it happens

Trigger: Invalid or expired API key (401/403); rate limiting (429); model name not available; quota exceeded; malformed request payload; baseUrl pointing to a wrong endpoint.

Common situations: API key rotated without updating config; wrong baseUrl (e.g. pointing to Azure endpoint without the OpenAI path); model deprecated; hitting rate limits during batch extraction; organization billing issues.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/96c6b6b03177cde5. Report an issue: GitHub.

Appendix: source

Thrown at apps/daemon/src/memory-llm.ts:847

            ? { 'APP-Code': AIHUBMIX_APP_CODE }
            : {}),
        },
        body: JSON.stringify({
          model: provider.model,
          response_format: { type: 'json_object' },
          messages: [
            { role: 'system', content: system },
            { role: 'user', content: user },
          ],
        }),
        signal: withTimeout(FETCH_TIMEOUT_MS),
      },
    );
  } catch (err) {
    throw new Error(describeFetchError(err));
  }
  if (!resp.ok) {
    throw new Error(`openai ${resp.status}: ${await resp.text().catch(() => '')}`);
  }
  const json = await resp.json();
  return json?.choices?.[0]?.message?.content ?? '';
}

// Azure OpenAI speaks the same chat-completions JSON as OpenAI, but on
// a per-deployment URL and with `api-key:` instead of `Authorization:`.
// `provider.model` here is the Azure deployment name (the user typed it
// into the model field — that's what the chat picker calls "Deployment
// (Model)" too), not the underlying model family.
async function callAzure(provider, system, user) {
  const base = String(provider.baseUrl || '').replace(/\/+$/, '');
  const deployment = encodeURIComponent(provider.model);
  const apiVersion = encodeURIComponent(
    provider.apiVersion || PROVIDER_DEFAULTS.azure.apiVersion,
  );
  const url = `${base}/openai/deployments/${deployment}/chat/completions?api-version=${apiVersion}`;
  let resp;

View on GitHub (pinned to 5be4028344)