{"record":{"id":"7241952827606673","repo":"koala73/worldmonitor","slug":"embed-map-frame-request-failed-resp-status","errorCode":null,"errorMessage":"Embed map frame request failed: ${resp.status}","messagePattern":"Embed map frame request failed: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/embed/embed-fetch.ts","lineNumber":186,"sourceCode":"  let target: string;\n  if (grant) {\n    headers['X-WorldMonitor-Grant'] = grant.token;\n    const url = new URL(EMBED_MAP_FRAME_PATH, origin);\n    url.searchParams.set('layers', canonicalizeEmbedLayers(layerIds).join(','));\n    target = url.toString();\n  } else {\n    target = `${origin}${EMBED_MAP_FRAME_PATH}?${publicFrameSearch(layerIds)}`;\n  }\n\n  const resp = await globalThis.fetch(target, {\n    method: 'GET',\n    headers,\n    credentials: 'omit',\n  });\n  if (resp.status === 429 || resp.status >= 500) {\n    throw new EmbedMapFrameUnavailableError(frameRetryAfterMs(resp));\n  }\n  if (!resp.ok) throw new Error(`Embed map frame request failed: ${resp.status}`);\n  return await resp.json() as EmbedMapFrameResponse;\n}\n\n/**\n * The cacheable query for the free layers among `layerIds`, falling back to\n * the plain uncacheable query when none qualify.\n *\n * Paid layers are dropped from the keyless URL rather than carried into it:\n * a keyless caller cannot receive them either way, and naming them would\n * multiply the shared key space past its seven free subsets. The frame marks\n * them un-ready from their absence in the response, which is the same outcome\n * an explicit `not-entitled` would produce.\n */\nfunction publicFrameSearch(layerIds: readonly EmbedLayerId[]): string {\n  const free = getEmbedPanelFreeTier('map');\n  const eligible = free\n    ? canonicalizeEmbedLayers(layerIds).filter((id) => free.layers.includes(id))\n    : [];","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/src/embed/embed-fetch.ts#L168-L204","documentation":"Generic failure path of fetchEmbedMapFrame: any response that is not ok and not 429/5xx (those route to EmbedMapFrameUnavailableError) throws this Error embedding the HTTP status code. It typically indicates a client-side problem such as 400 (bad query), 401/403 (auth), or 404 (unknown layers/frame).","triggerScenarios":"Embed frame endpoint responds 400/401/403/404 or other 4xx: malformed layerIds or query parameters, missing/invalid embed key or auth header, or a frame id that does not exist.","commonSituations":"Caller passing layer ids the endpoint does not recognize after a rename; expired or revoked embed key sent in headers; constructing the target URL with unencoded characters; requesting a frame configuration that was deleted.","solutions":["Read the status code in the message: 400 → fix query/layerIds; 401/403 → renew or correctly attach the embed key; 404 → the frame/layers no longer exist.","Log the full target URL and headers (redacting secrets) and validate layerIds against the current API.","Regenerate the embed key if it was revoked and update the consumer.","URL-encode all query parameters when building the request."],"exampleFix":"// before\nconst target = `${base}/frame?layers=${layerIds.join(',')}`;\n// after\nconst target = `${base}/frame?${new URLSearchParams({ layers: layerIds.join(',') })}`;\n// and handle 4xx explicitly:\nif (!resp.ok && resp.status !== 429 && resp.status < 500) {\n  throw new Error(`Embed map frame request failed: ${resp.status} — check layerIds and embed key`);\n}","handlingStrategy":"try-catch","validationCode":"const target = `${base}/frame?${new URLSearchParams({ layers: layerIds.join(',') })}`;\nif (!knownLayerIds.every(id => validLayerIds.has(id))) throw new Error('Unknown layerIds');\nif (!embedKey) throw new Error('Embed key missing — create one first');","typeGuard":null,"tryCatchPattern":"try {\n  const frame = await fetchEmbedMapFrame(params);\n} catch (e) {\n  const m = /failed: (\\d{3})/.exec(e.message);\n  if (m) {\n    const code = Number(m[1]);\n    if (code === 401 || code === 403) promptEmbedKeyRenewal();\n    else if (code === 404) refreshLayerCatalog();\n    else if (code === 400) console.error('Bad query:', params);\n  } else throw e;\n}","preventionTips":["URLSearchParams for query construction; never hand-concatenate.","Validate layerIds against the current catalog before requesting.","Rotate embed keys proactively and handle 401/403 with a renewal flow."],"tags":["http","embed","client-error","authentication"],"backgroundTag":"http-error-response","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}