farion1231/cc-switch · error · Error

Provider key is required for ${appId}

Error message

Provider key is required for ${appId}

What it means

Thrown inside useAddProviderMutation's mutationFn for appId 'opencode', 'openclaw', 'hermes', or 'pi' when category is not 'omo'/'omo-slim' and providerKey is empty. For these apps the providerKey IS the persisted provider id (it must match the key in the target app's own config, e.g. opencode providers.json), so it cannot be auto-generated like a UUID.

Source

Thrown at src/lib/query/mutations.ts:111

      }

      let id: string;

      if (
        appId === "opencode" ||
        appId === "openclaw" ||
        appId === "hermes" ||
        appId === "pi"
      ) {
        if (
          providerInput.category === "omo" ||
          providerInput.category === "omo-slim"
        ) {
          const prefix = providerInput.category === "omo" ? "omo" : "omo-slim";
          id = `${prefix}-${generateUUID()}`;
        } else {
          if (!providerInput.providerKey) {
            throw new Error(`Provider key is required for ${appId}`);
          }
          id = providerInput.providerKey;
        }
      } else {
        id = generateUUID();
      }

      const newProvider: Provider = {
        ...rest,
        id,
        createdAt: Date.now(),
      };
      delete (newProvider as any).providerKey;

      await providersApi.add(newProvider, appId, addToLive);
      return newProvider;
    },
    onSuccess: async () => {

View on GitHub (pinned to a2e22f3302)

Solutions

  1. Supply a providerKey matching the target app's provider key (e.g. 'anthropic', 'my-proxy')
  2. Or set category to 'omo'/'omo-slim', which auto-generates a prefixed UUID id instead
  3. Trim and require the key in the form before submitting the mutation

Example fix

// before
addProvider.mutateAsync({ appId: "opencode", providerInput: { category: "custom", name: "My proxy" } });

// after
addProvider.mutateAsync({ appId: "opencode", providerInput: { category: "custom", providerKey: "my-proxy", name: "My proxy" } });
Defensive patterns

Strategy: validation

Validate before calling

const KEYED_APPS = new Set(["opencode", "openclaw", "hermes", "pi"]);

function requiresProviderKey(appId: string, category?: string): boolean {
  return (
    KEYED_APPS.has(appId) &&
    category !== "omo" &&
    category !== "omo-slim"
  );
}

if (requiresProviderKey(appId, input.category) && !input.providerKey?.trim()) {
  setFormError("Provider key is required for this app");
  return;
}

Type guard

function isKeyedProviderInput(
  i: { category?: string; providerKey?: string },
): i is { category: string; providerKey: string } {
  return Boolean(i.providerKey && i.providerKey.trim().length > 0);
}

Try / catch

try {
  await addProvider.mutateAsync(input);
} catch (e) {
  if (e instanceof Error && e.message.startsWith("Provider key is required")) {
    focusProviderKeyField();
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: mutateAsync({ appId: 'opencode', providerInput: { category: 'custom', providerKey: '' } }) - any non-omo category with a missing or whitespace-only providerKey on those four app ids.

Common situations: A provider form that forgets to carry the key field; importing providers from apps that use generated UUID ids; trimmed empty key slipping through.

Related errors


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