Mintplex-Labs/anything-llm · warning · Error

HTTP

Error message

HTTP ${resp.status}: ${(await resp.text()).slice(0, 200)}

What it means

Thrown by the interface-service prompt preflight classifier when POST ${OPENAI_BASE_URL||https://api.openai.com/v1}/chat/completions returns a non-2xx status; the message embeds the status and the first 200 bytes of the response body. The call is an OpenAI-compatible chat completion with tool_choice 'required' used to route prompts to simple/plan_first/delegate_sequential modes. Importantly the surrounding try/catch downgrades this to a console.warn and a 'simple' routing decision, so the system keeps working — this error degrades planning quality rather than breaking the request.

Solutions

  1. Check the status/body in the message: 401 -> fix OPENAI_API_KEY; 404 -> fix OPENAI_BASE_URL (include /v1) or OPENAI_MODEL; 429 -> wait/raise limits
  2. Verify the chat/completions endpoint works with curl from the same machine the service runs on, including the tools+tool_choice payload
  3. If using a local gateway, confirm it is bound to an address reachable via resolveBaseUrlForGuest (not 127.0.0.1 of the host only)
  4. If you don't need planning, set SKIP_PLANNING=1 or PROMPT_PREFLIGHT=false — the preflight is skipped and this call never happens

Example fix

# before: OPENAI_BASE_URL=http://localhost:1234 (missing /v1) -> HTTP 404
export OPENAI_BASE_URL=http://localhost:1234
# after
export OPENAI_BASE_URL=http://localhost:1234/v1
export OPENAI_MODEL=qwen2.5:14b   # a model the gateway actually serves
Defensive patterns

Strategy: fallback

Validate before calling

async function preflightReachable(baseUrl, apiKey, model) {
  try {
    const r = await fetch(`${baseUrl.replace(/\/$/, '')}/chat/completions`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}) },
      body: JSON.stringify({ model, messages: [{ role: 'user', content: 'ping' }], max_tokens: 1 }),
    });
    return r.ok;
  } catch { return false; }
}
// run once at service start; log loudly if false

Try / catch

// Already handled internally: runPromptPreflight catches and returns { mode: 'simple', reason: `failed: ...` }.
// In your own wrapper, mirror it:
try { return await runPromptPreflight(prompt); } catch { return { mode: 'simple', reason: 'preflight unavailable' }; }

Prevention

When it happens

Trigger: settings.OPENAI_API_KEY invalid or expired (401); OPENAI_BASE_URL wrong or missing the /v1 suffix (404); OPENAI_MODEL set to a name the endpoint doesn't serve (404/400); rate limits or quota exhausted (429/402); self-hosted gateway (LM Studio/Ollama/vLLM) reachable from the host but not from resolveBaseUrlForGuest's guest-side address (connection errors surface similarly); endpoints that reject the 'tools'+tool_choice payload (400).

Common situations: Swapping the model provider without updating OPENAI_BASE_URL/OPENAI_MODEL; expired API key in the service settings; local LLM server not running when the agent starts; hitting provider rate limits during heavy automated sessions.

Related errors


AI-assisted analysis of Mintplex-Labs/anything-llm@3aec848f28 (2026-08-18). Data as JSON: /api/errors/11842e6e82e70d9f. Report an issue: GitHub.

Appendix: source

Thrown at open-computer/services/interface-service/preflight/index.js:150

                    type: "array",
                    items: { type: "string" },
                    description:
                      "For plan_first or delegate_sequential: 3-6 compact bullets to guide execution.",
                  },
                },
                required: ["mode", "reason"],
              },
            },
          },
        ],
        tool_choice: "required",
      }),
    });

    clearTimeout(timeout);

    if (!resp.ok) {
      throw new Error(
        `HTTP ${resp.status}: ${(await resp.text()).slice(0, 200)}`,
      );
    }

    const data = await resp.json();
    const args = parsePreflightToolArgs(data.choices?.[0]?.message);
    const mode =
      args.mode === "delegate_sequential"
        ? "delegate_sequential"
        : args.mode === "plan_first"
          ? "plan_first"
          : "simple";
    return {
      mode,
      reason: String(args.reason || "model decision").slice(0, 200),
      plan_hint: mode !== "simple" ? normalizePlanHint(args.plan_hint) : [],
    };
  } catch (err) {

View on GitHub (pinned to 3aec848f28)