koala73/worldmonitor · error · ValidationError

Unsupported metal

Error message

Unsupported metal${unsupported.length === 1 ? '' : 's'}: ${unsupported.join(', ')}

What it means

Validation in resolvePhysicalPremiumMetals: one or more requested metals (listed in the message) are not in SUPPORTED_METALS after trim and lowercase normalization — including blank entries, reported as '<blank>'. The whole request is rejected rather than silently dropping the unsupported metals.

Solutions

  1. Request only supported metals; check the message for the exact unsupported labels
  2. Trim and validate the metals list client-side against the supported set before calling
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at server/worldmonitor/market/v1/get-physical-premiums.ts:159 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of koala73/worldmonitor@e586b8b4b8 (2026-08-21). Data as JSON: /api/errors/9f8e48d16bc4252e. Report an issue: GitHub.

Appendix: source

Thrown at server/worldmonitor/market/v1/get-physical-premiums.ts:160

    || !isoInstant(raw.asOf)
  ) return null;
  return { pair: raw.pair, rate: raw.rate, source: raw.source, asOf: raw.asOf };
}

export function resolvePhysicalPremiumMetals(rawMetals: string[]): string[] {
  const metals: string[] = [];
  const unsupported: string[] = [];
  for (const raw of rawMetals) {
    const metal = raw.trim().toLowerCase();
    if (!metal || !SUPPORTED_METALS.has(metal)) {
      const label = metal || '<blank>';
      if (!unsupported.includes(label)) unsupported.push(label);
      continue;
    }
    if (!metals.includes(metal)) metals.push(metal);
  }
  if (unsupported.length > 0) {
    throw new ValidationError([{
      field: 'metals',
      description: `Unsupported metal${unsupported.length === 1 ? '' : 's'}: ${unsupported.join(', ')}`,
    }]);
  }
  return metals;
}

export async function getPhysicalPremiums(
  _ctx: ServerContext,
  req: GetPhysicalPremiumsRequest,
): Promise<GetPhysicalPremiumsResponse> {
  const metals = resolvePhysicalPremiumMetals(parseStringArray(req.metals));
  const read = await readCachedJson(PHYSICAL_PREMIUM_KEY, true);
  if (read.status === 'error') throw new SeedUnavailableError(PHYSICAL_PREMIUM_KEY);
  try {
    const raw = read.status === 'hit' ? read.value as RawPayload | null : null;
    const fx = mapFx(raw?.fx);
    if (!fx || !Array.isArray(raw?.premiums)) return { premiums: [] };

View on GitHub (pinned to e586b8b4b8)