tinyhumansai/openhuman · warning

settings.ai.slugMissingError | settings.ai.slugInUseError |

Error message

settings.ai.slugMissingError | settings.ai.slugInUseError | settings.ai.slugReservedError

What it means

submitProvider re-throws a precomputed slugError before doing anything else: the custom provider slug must be non-empty (slugMissingError), must not collide with another provider's slug (slugInUseError), and — when creating, not editing — must not match a builtin (slugReservedError, from BUILTIN_RESERVED_SLUGS).

Source

Thrown at app/src/components/settings/panels/AIPanel.tsx:3906

  // Skipping verification is a bet that the provider works despite an
  // unreadable listing. For an Azure host that is not the `/openai/v1` base
  // that bet is already lost: `{base}/chat/completions` is not a route Azure
  // serves there and the stored bearer auth is the wrong header, so the entry
  // would be dead on arrival. Withhold the bypass and let the inline nudge do
  // its job instead of manufacturing a broken provider (#5213).
  const knownUnusableEndpoint =
    isAzureFoundryEndpoint(endpoint) && !isAzureV1BaseUrl(endpoint.trim());

  const submitProvider = async (opts?: { skipProbe?: boolean }) => {
    setSaving(true);
    setSubmitError(null);
    // Cleared alongside the error: a later attempt that fails for an unrelated
    // reason (slug collision, key write) must not still offer to skip
    // verification, which is the distinction `ProviderProbeError` exists for.
    setProbeFailed(false);
    try {
      if (slugError) {
        throw new Error(slugError);
      }
      await onSubmit(
        {
          id: initial?.id ?? '',
          slug,
          label: label.trim() || slug,
          endpoint: endpoint.trim(),
          authStyle: initial?.authStyle ?? 'bearer',
          maskedKey: maskKeyLabel(hasExistingKey || apiKey.length > 0),
        },
        apiKey.trim(),
        opts
      );
    } catch (err) {
      // Surface the failure inline and keep the dialog open so the user can fix
      // the key/URL and retry. A rejected `/models` probe additionally unlocks
      // the "add without verifying" button — the listing is a convenience for
      // the model dropdown, not a precondition for inference.

View on GitHub (pinned to a221052e0d)

Solutions

  1. Enter a unique, non-empty slug that does not match a builtin provider name
  2. Rename or delete the colliding custom provider first
  3. Check the live slugError indicator next to the field before submitting
Defensive patterns

Strategy: validation

Validate before calling

// Compute the same three checks before enabling submit:
const slugOk =
  slug.trim().length > 0 &&
  !existingSlugs.includes(slug) &&
  (isEditing || !BUILTIN_RESERVED_SLUGS.includes(slug));
if (!slugOk) { /* disable submit and show which rule failed */ }

Type guard

const isUsableSlug = (
  slug: string,
  existing: readonly string[],
  reserved: readonly string[],
  isEdit: boolean
): boolean =>
  slug.trim().length > 0 &&
  !existing.includes(slug) &&
  (isEdit || !reserved.includes(slug));

Try / catch

try {
  await submitProvider({ skipProbe });
} catch (e) {
  if (e instanceof Error && e.message === slugError) {
    // keep dialog open focused on the slug field
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Submitting the custom-provider form with an empty slug, a slug already used by an existing entry, or a slug matching a reserved builtin name.

Common situations: User duplicates an existing provider config and forgets to rename the slug; tries to shadow a builtin provider by reusing its slug.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16). Data as JSON: /api/errors/054f14cfa2d22243. Report an issue: GitHub.