koala73/worldmonitor · error · MissionPresetCatalogError

invalid_variant

invalid_variant

Error message

variant must be one of: full, tech, finance, commodity, energy, happy.

What it means

listMissionPresetCatalog() reads live.variant from the caller-supplied MissionPresetCatalogLiveState and verifies it with isSiteVariant(). Because live.variant is typed as a plain string, an unrecognized variant string makes downstream variant-filtered preset lookups impossible, so the function throws MissionPresetCatalogError with reason 'invalid_variant'. Valid variants are: full, tech, finance, commodity, energy, happy.

Source

Thrown at src/services/webmcp-mission-preset-catalog.ts:209

      }
      : {
        panelCount: 0,
        layerCount: 0,
      }),
    active: live.activePresetId === preset.id,
    monitorCompatible,
    entitled,
    available,
    ...(reason ? { unavailableReason: reason } : {}),
  };
}

export function listMissionPresetCatalog(
  live: MissionPresetCatalogLiveState,
  query: MissionPresetCatalogQuery = {},
): MissionPresetCatalogResult {
  if (!isSiteVariant(live.variant)) {
    throw new MissionPresetCatalogError(
      'invalid_variant',
      'variant must be one of: full, tech, finance, commodity, energy, happy.',
    );
  }
  if (query.available !== undefined && typeof query.available !== 'boolean') {
    throw new MissionPresetCatalogError(
      'malformed_arguments',
      'available must be a boolean.',
    );
  }

  const presets = getMissionPresetsForVariant(live.variant)
    .map((preset) => buildMissionPresetCatalogItem(preset.id, {
      ...live,
      variant: live.variant as SiteVariant,
    }))
    .filter((item) => (query.available === undefined ? true : item.available === query.available));

View on GitHub (pinned to 9361220cc0)

Solutions

  1. Set live.variant to one of the SITE_VARIANTS values ('full','tech','finance','commodity','energy','happy'); check @/config/variant for the authoritative list.
  2. Normalize the incoming value: trim and lowercase it, map legacy alias names to current variant ids before building the live state.
  3. Run isSiteVariant(variant) on the value before constructing MissionPresetCatalogLiveState and fall back to VARIANT_DEFAULTS/default variant when false.
  4. Catch MissionPresetCatalogError and inspect reason === 'invalid_variant' to surface the accepted variant list to the caller.

Example fix

// before
const live = { variant: urlParams.get('variant') ?? '', hasPremium: false, activePresetId: null };
listMissionPresetCatalog(live);
// after
const raw = urlParams.get('variant')?.toLowerCase() ?? '';
if (!isSiteVariant(raw)) throw new Error(`unsupported variant: ${raw}`);
listMissionPresetCatalog({ ...live, variant: raw });
Defensive patterns

Strategy: validation

Validate before calling

import { isSiteVariant } from '@/config/variant';
if (!isSiteVariant(live.variant)) {
  throw new Error(`variant must be one of full|tech|finance|commodity|energy|happy, got '${live.variant}'`);
}

Type guard

function isKnownVariant(value: string): value is SiteVariant {
  return isSiteVariant(value);
}

Try / catch

try {
  return listMissionPresetCatalog(live, query);
} catch (err) {
  if (err instanceof MissionPresetCatalogError && err.reason === 'invalid_variant') {
    return listMissionPresetCatalog({ ...live, variant: DEFAULT_VARIANT }, query);
  }
  throw err;
}

Prevention

When it happens

Trigger: Passing a MissionPresetCatalogLiveState whose variant is not exactly one of 'full' | 'tech' | 'finance' | 'commodity' | 'energy' | 'happy' — e.g. 'prod', 'default', 'Full', an empty string, or undefined coerced to a string — when listMissionPresetCatalog is invoked (via listWebMcpMissionPresets).

Common situations: Constructing the live state from a URL query param, env var, or host-app setting with a different naming scheme than SITE_VARIANTS; a new variant added in the host app but not registered in @/config/variant; case-sensitive mismatch ('Tech' vs 'tech').

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of koala73/worldmonitor@9361220cc0 (2026-09-01). Data as JSON: /api/errors/003f44a5ed84f2f1. Report an issue: GitHub.