{"record":{"id":"9f8e48d16bc4252e","repo":"koala73/worldmonitor","slug":"unsupported-metal-unsupported-length-1","errorCode":null,"errorMessage":"Unsupported metal${unsupported.length === 1 ? '' : 's'}: ${unsupported.join(', ')}","messagePattern":"Unsupported metal(.+?): (.+?)","errorType":"validation","errorClass":"ValidationError","httpStatus":400,"severity":"error","filePath":"server/worldmonitor/market/v1/get-physical-premiums.ts","lineNumber":159,"sourceCode":"    || !isoInstant(raw.asOf)\n  ) return null;\n  return { pair: raw.pair, rate: raw.rate, source: raw.source, asOf: raw.asOf };\n}\n\nexport function resolvePhysicalPremiumMetals(rawMetals: string[]): string[] {\n  const metals: string[] = [];\n  const unsupported: string[] = [];\n  for (const raw of rawMetals) {\n    const metal = raw.trim().toLowerCase();\n    if (!metal || !SUPPORTED_METALS.has(metal)) {\n      const label = metal || '<blank>';\n      if (!unsupported.includes(label)) unsupported.push(label);\n      continue;\n    }\n    if (!metals.includes(metal)) metals.push(metal);\n  }\n  if (unsupported.length > 0) {\n    throw new ValidationError([{\n      field: 'metals',\n      description: `Unsupported metal${unsupported.length === 1 ? '' : 's'}: ${unsupported.join(', ')}`,\n    }]);\n  }\n  return metals;\n}\n\nexport async function getPhysicalPremiums(\n  _ctx: ServerContext,\n  req: GetPhysicalPremiumsRequest,\n): Promise<GetPhysicalPremiumsResponse> {\n  const metals = resolvePhysicalPremiumMetals(parseStringArray(req.metals));\n  try {\n    const raw = await getCachedJson(PHYSICAL_PREMIUM_KEY, true) as RawPayload | null;\n    const fx = mapFx(raw?.fx);\n    if (!fx || !Array.isArray(raw?.premiums)) return { premiums: [] };\n    const premiums = raw.premiums.map((premium) => mapPremium(premium, fx.rate))\n      .filter((item): item is PhysicalPremium => item !== null);","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/koala73/worldmonitor/blob/eeab0a219fce0f02a00603b532dbae9041b934ac/server/worldmonitor/market/v1/get-physical-premiums.ts#L141-L177","documentation":"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.","triggerScenarios":"Thrown at server/worldmonitor/market/v1/get-physical-premiums.ts:159 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Request only supported metals; check the message for the exact unsupported labels","Trim and validate the metals list client-side against the supported set before calling"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"eeab0a219fce0f02a00603b532dbae9041b934ac","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}