farion1231/cc-switch · warning · PiFormValidationError

pi.form.effectiveApiRequired

Error message

pi.form.effectiveApiRequired

What it means

Thrown per-model in PiProviderForm's submit() when creating (!isEdit) with neither a per-model passthrough 'api' override (model.passthrough.api) nor a provider-level api selected (PiProviderForm.tsx:1174-1180). Pi needs a wire format (API family) to talk to the endpoint. Edit mode is exempt by design: existing explicit nodes may be partial overrides of a Pi built-in provider and inherit its transport (comment at lines 1170-1173). The message interpolates the model id; fieldSelector '#pi-provider-api-select' with revealAdvanced=true focuses the API selector.

Source

Thrown at src/components/providers/forms/PiProviderForm.tsx:1181

            true,
          );
        }
        // Pi's schema supports rare per-model api/baseUrl overrides. Keep
        // imported values losslessly, but use the provider-level format and
        // endpoint as the normal product model.
        const modelApi =
          typeof model.passthrough.api === "string"
            ? model.passthrough.api.trim()
            : "";
        const modelBaseUrl =
          typeof model.passthrough.baseUrl === "string"
            ? model.passthrough.baseUrl.trim()
            : "";
        // Existing explicit nodes may be partial overrides of a Pi built-in
        // provider. Pi inherits the built-in transport in that case, so only
        // require a complete transport when CC Switch creates a new provider.
        if (!isEdit && !modelApi && !api.trim()) {
          throw new PiFormValidationError(
            t("pi.form.effectiveApiRequired", { id }),
            "#pi-provider-api-select",
            true,
          );
        }
        const effectiveUrl = modelBaseUrl || baseUrl.trim();
        if (!isEdit && !effectiveUrl) {
          throw new PiFormValidationError(
            t("pi.form.effectiveBaseUrlRequired", { id }),
            "#pi-provider-base-url",
          );
        }
        return {
          ...model.passthrough,
          id,
          ...(includeName ? { name: displayName } : {}),
          ...(includeReasoning ? { reasoning: model.reasoning } : {}),
          ...(includeInput

View on GitHub (pinned to a2e22f3302)

Solutions

  1. Pick the API format (e.g. the OpenAI- or Anthropic-compatible family) in the provider's advanced/API settings, then save
  2. Alternatively set a per-model passthrough "api" string in the model's advanced JSON
  3. When building custom flows, default api from the selected preset's template values

Example fix

// before
const values = { /* ... */ api: "" };

// after
const values = { /* ... */ api: "openai" }; // or "anthantic"-family per your endpoint; must match a known Pi api format
Defensive patterns

Strategy: validation

Validate before calling

const modelApi = typeof model.passthrough.api === "string" ? model.passthrough.api.trim() : "";
if (!isEdit && !modelApi && api.trim() === "") {
  setFormError(t("pi.form.effectiveApiRequired", { id: model.id }));
  return;
}

Try / catch

try {
  await submit(identity);
} catch (error) {
  if (error instanceof Error && error.name === "PiFormValidationError") { /* focuses '#pi-provider-api-select' after revealing advanced settings */ return; }
  throw error;
}

Prevention

When it happens

Trigger: Creating a provider, leaving the API protocol dropdown empty, and not setting 'api' in any model's advanced passthrough JSON; selecting 'Custom' preset which does not prefill an api format.

Common situations: User assumes baseUrl alone is enough; preset template lacked an api value; per-model overrides were cleared during import; user confusion between the provider-level selector and the per-model passthrough field.

Related errors


AI-assisted analysis of farion1231/cc-switch@a2e22f3302 (2026-08-16). Data as JSON: /api/errors/91973c8ec864c45d. Report an issue: GitHub.