paperclipai/paperclip · warning
Could not load OpenRouter models. Retry or enter a model ID…
Error message
Could not load OpenRouter models. Retry or enter a model ID manually.
What it means
listOpenRouterModels fetches OpenRouter's public model catalog (https://openrouter.ai/api/v1/models) with a 10s timeout. If the HTTP response status is not ok, it throws 'Could not load OpenRouter models. Retry or enter a model ID manually.' The message is user-facing: the catalog could not be fetched, but a model ID can still be entered manually.
Solutions
- Retry the request (call listOpenRouterModels(true) to force refresh) — the error message itself suggests retrying.
- Enter a model ID manually in the agent configuration instead of picking from the catalog.
- Check network egress to https://openrouter.ai/api/v1/models (curl it to confirm reachability).
- If OpenRouter is rate limiting, wait and retry; note successful results are cached for 60 seconds.
Example fix
// before
const models = await listOpenRouterModels();
// after
let models;
try {
models = await listOpenRouterModels();
} catch {
models = []; // fall back to manual model ID entry in the UI
} Defensive patterns
Strategy: retry
Validate before calling
const res = await fetch("https://openrouter.ai/api/v1/models", { signal: AbortSignal.timeout(10_000) });
if (!res.ok) console.warn("openrouter catalog unreachable, status", res.status); Try / catch
let models = [];
for (let attempt = 0; attempt < 3 && models.length === 0; attempt++) {
try { models = await listOpenRouterModels(attempt > 0); }
catch (e) { if (!e.message.includes("OpenRouter models")) throw e; await sleep(2 ** attempt * 500); }
} Prevention
- Verify egress to openrouter.ai from the deployment environment.
- Cache successful catalog fetches (the service caches 60s) and serve stale data on failure.
- Always offer manual model-ID entry as a fallback in the UI.
When it happens
Trigger: Calling the OpenRouter models endpoint (via agentRoutes) when the upstream returns a non-2xx status: network outage, OpenRouter 429/5xx, blocked egress, or DNS failure with a non-ok response.
Common situations: Corporate firewall blocking openrouter.ai; OpenRouter outage or rate limiting; request exceeding the 10-second AbortSignal timeout in an environment with slow egress.
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
- Anthropic Managed Agents request failed with HTTP
- Artifact download failed: HTTP
- Discord returned an unreadable response
- Email attachment unavailable
- OpenCode API request failed
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/4bd2cdcc91cccf52.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/openrouter-models.ts:12
import type { AdapterModel } from "@paperclipai/adapter-utils";
let cached: { until: number; models: AdapterModel[] } | undefined;
let pending: Promise<AdapterModel[]> | undefined;
/** OpenRouter's public catalog does not require access to anyone's credentials. */
export async function listOpenRouterModels(refresh = false): Promise<AdapterModel[]> {
if (!refresh && cached && cached.until > Date.now()) return cached.models;
if (pending) return pending;
pending = (async () => {
const response = await fetch("https://openrouter.ai/api/v1/models", { signal: AbortSignal.timeout(10_000) });
if (!response.ok) throw new Error("Could not load OpenRouter models. Retry or enter a model ID manually.");
const body = await response.json() as { data?: Array<{ id?: unknown; name?: unknown }> };
if (!Array.isArray(body.data)) throw new Error("OpenRouter returned an invalid model catalog.");
const models = body.data.flatMap(model => typeof model.id === "string" && model.id.includes("/")
? [{ id: `openrouter/${model.id}`, label: typeof model.name === "string" ? model.name : model.id }]
: []).sort((a, b) => a.label.localeCompare(b.label));
cached = { until: Date.now() + 60_000, models };
return models;
})();
try { return await pending; } finally { pending = undefined; }
}
View on GitHub (pinned to 3f1d897a7c)