midudev/autoskills · error · Error

OpenAI

Error message

OpenAI ${res.status}: ${text.slice(0, 200)}

What it means

When the OpenAI chat-completions API responds with a non-2xx status, reviewWithOpenAI reads the response body and throws `OpenAI <status>: <first 200 chars>` so the developer sees the API's own error message (quota exceeded, invalid key, model not found, etc.). It surfaces upstream HTTP failures with truncated context.

Solutions

  1. Read the status/message: 401 → fix OPENAI_API_KEY; 429 → wait and retry or add credits; 404 → fix the model name
  2. Verify the API key works: curl https://api.openai.com/v1/models with the Bearer token
  3. Check billing/quota at platform.openai.com and top up if needed
  4. Add retry-with-backoff around review for transient 429/5xx, or skip with --no-review

Example fix

// before
const res = await fetch(url, { headers, body: JSON.stringify({ model: "gpt-4-turbo-preview", ... }) }); // 404: model retired
// after
const res = await fetch(url, { headers, body: JSON.stringify({ model: "gpt-4o", ... }) }); // valid model
Defensive patterns

Strategy: retry

Validate before calling

// validate the key and model before the review call
const keyCheck = await fetch("https://api.openai.com/v1/models", { headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}` } });
if (!keyCheck.ok) throw new Error(`OpenAI key invalid or quota exceeded: ${keyCheck.status}`);

Try / catch

try {
  await reviewWithOpenAI(skillName, files);
} catch (err) {
  const m = err.message.match(/^OpenAI (\d{3}):/);
  if (m) {
    const status = Number(m[1]);
    if (status === 401) console.error("Fix OPENAI_API_KEY");
    else if (status === 429) await retryWithBackoff(() => reviewWithOpenAI(skillName, files));
    else if (status === 404) console.error("Model name invalid/retired — update configuration");
  } else throw err;
}

Prevention

When it happens

Trigger: POST to the OpenAI API returns res.ok === false — 401 invalid/revoked key, 429 rate limit or insufficient quota, 404 wrong model name, 5xx OpenAI outage. Body text is captured and truncated to 200 chars.

Common situations: Expired or revoked OPENAI_API_KEY; billing/quota exhausted on the account; typos or deprecation in the configured model name; very large reviews hitting rate limits (429).

Related errors


AI-assisted analysis of midudev/autoskills@0ec725320d (2026-09-15). Data as JSON: /api/errors/6b03e5a976843fc1. Report an issue: GitHub.

Appendix: source

Thrown at packages/autoskills/scripts/sync-skills.mjs:522

  const res = await fetch("https://api.openai.com/v1/chat/completions", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${apiKey}`,
    },
    body: JSON.stringify({
      model: REVIEW_MODEL,
      response_format: { type: "json_object" },
      messages: [
        { role: "system", content: REVIEW_SYSTEM_PROMPT },
        { role: "user", content: userMsg },
      ],
    }),
  });

  if (!res.ok) {
    const text = await res.text().catch(() => "");
    throw new Error(`OpenAI ${res.status}: ${text.slice(0, 200)}`);
  }
  const payload = await res.json();
  const raw = payload.choices?.[0]?.message?.content ?? "{}";
  let parsed;
  try {
    parsed = JSON.parse(raw);
  } catch {
    return {
      status: "rejected",
      flags: ["invalid-json"],
      summary: "auditor returned invalid JSON",
    };
  }
  const status = ["approved", "flagged", "rejected"].includes(parsed.status)
    ? parsed.status
    : "rejected";
  const flags = Array.isArray(parsed.flags) ? parsed.flags.map(String) : [];
  const summary = typeof parsed.summary === "string" ? parsed.summary : "";

View on GitHub (pinned to 0ec725320d)