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

  1. Configure at least one provider in the Codewhale Runtime config and restart `codewhale web`
  2. Inspect GET /v1/providers and confirm each entry has a non-empty `id`
  3. Fix malformed provider entries (missing id) that the client filter drops
  4. 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

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


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)