Hmbown/CodeWhale · error · Error
The Runtime returned no providers.
Error message
The Runtime returned no providers.
What it means
Thrown when the /v1/providers catalog request succeeds but, after filtering out entries without a usable id, no providers remain. The new-thread dialog cannot offer any provider/model choice, so the loader fails loudly rather than rendering an empty picker.
Solutions
- Configure at least one provider in the Codewhale Runtime config and restart `codewhale web`
- Inspect GET /v1/providers and confirm each entry has a non-empty `id`
- Fix malformed provider entries (missing id) that the client filter drops
- Verify the Runtime build matches the browser bundle's expected catalog shape
Example fix
// before: rendering whatever arrives and failing later
app.providerCatalog = catalog;
// after: fail loud on an empty catalog, as the loader does
const providers = (catalog?.providers || []).filter(p => String(p?.id || '').trim());
if (!providers.length) throw new Error('no usable providers; check Runtime provider config'); Defensive patterns
Strategy: try-catch
Validate before calling
const providers = (catalog?.providers || []).filter(p => String(p?.id || '').trim()); if (providers.length === 0) showConfigWarning();
Type guard
const hasProviders = (c) => Array.isArray(c?.providers) && c.providers.some(p => String(p?.id || '').trim());
Try / catch
try { await openNewThreadDialog(); } catch (e) { if (String(e.message).includes('no providers')) showToast('Configure a provider in the Runtime config.'); } Prevention
- Configure at least one provider before launching the web UI
- Give every provider entry a non-empty id
- Validate Runtime provider config at startup
- Verify /v1/providers returns usable entries after config changes
When it happens
Trigger: api('/v1/providers') returns a catalog whose `providers` is not an array or contains only entries with blank/missing `id`; the filtered providers.length === 0 path throws.
Common situations: No providers configured in the Runtime (empty config); malformed provider entries in the Runtime config causing them to be filtered out; a Runtime build returning a different catalog payload shape; a proxy returning a valid-JSON but empty body.
Understand the failure class
Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.
Related errors
- 127
- A pinned task provider requires an explicit model
- agent profile reasoning_effort must be one of: inherit…
- approval log has no parent
- --bridge is required in --non-interactive mode.
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/6096660360477bf2.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/runtime_web/app.mjs:2001
dom.newThreadDialog.showModal();
dom.newThreadCancel.focus({ preventScroll: true });
const generation = ++app.newThreadGeneration;
app.providerCatalog = null;
app.newThreadModels = [];
app.newThreadLoading = true;
dom.newThreadProvider.replaceChildren();
dom.newThreadModel.replaceChildren();
dom.newThreadModelInput.value = "";
setNewThreadStatus("Loading providers…");
renderNewThreadCapability();
syncNewThreadControls();
try {
const catalog = await api("/v1/providers");
if (generation !== app.newThreadGeneration || !dom.newThreadDialog.open) return;
const providers = Array.isArray(catalog?.providers)
? catalog.providers.filter((provider) => String(provider?.id || "").trim())
: [];
if (providers.length === 0) throw new Error("The Runtime returned no providers.");
app.providerCatalog = { ...catalog, providers };
for (const provider of providers) {
const option = document.createElement("option");
option.value = provider.id;
setSafeText(option, providerOptionLabel(provider));
dom.newThreadProvider.append(option);
}
const defaults = newThreadDefaults(app.providerCatalog);
dom.newThreadProvider.value = defaults.providerId;
dom.newThreadProvider.disabled = false;
dom.newThreadProvider.focus({ preventScroll: true });
await loadNewThreadModels(defaults.providerId, defaults.model, generation);
} catch (error) {
if (generation !== app.newThreadGeneration || !dom.newThreadDialog.open) return;
app.newThreadLoading = false;
setNewThreadStatus(`Could not load providers: ${error.message}`, "error");
syncNewThreadControls();
}View on GitHub (pinned to 73e0f67d83)