koala73/worldmonitor · error
Embed map frame request failed: ${resp.status}
Error message
Embed map frame request failed: ${resp.status} What it means
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).
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.
Example fix
// before
const target = `${base}/frame?layers=${layerIds.join(',')}`;
// after
const target = `${base}/frame?${new URLSearchParams({ layers: layerIds.join(',') })}`;
// and handle 4xx explicitly:
if (!resp.ok && resp.status !== 429 && resp.status < 500) {
throw new Error(`Embed map frame request failed: ${resp.status} — check layerIds and embed key`);
} Defensive patterns
Strategy: try-catch
Validate before calling
const target = `${base}/frame?${new URLSearchParams({ layers: layerIds.join(',') })}`;
if (!knownLayerIds.every(id => validLayerIds.has(id))) throw new Error('Unknown layerIds');
if (!embedKey) throw new Error('Embed key missing — create one first'); Try / catch
try {
const frame = await fetchEmbedMapFrame(params);
} catch (e) {
const m = /failed: (\d{3})/.exec(e.message);
if (m) {
const code = Number(m[1]);
if (code === 401 || code === 403) promptEmbedKeyRenewal();
else if (code === 404) refreshLayerCatalog();
else if (code === 400) console.error('Bad query:', params);
} else throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- Cloudflare Radar API error: ${resp.status}
- Embed map frame temporarily unavailable
- ${operation} HTTP ${status}: ${safeCode}
- Exa ${endpoint.slice(1)} failed HTTP ${response.status}: ${d
- unauthorized
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/7241952827606673.
Report an issue: GitHub.
Appendix: source
Thrown at src/embed/embed-fetch.ts:186
let target: string;
if (grant) {
headers['X-WorldMonitor-Grant'] = grant.token;
const url = new URL(EMBED_MAP_FRAME_PATH, origin);
url.searchParams.set('layers', canonicalizeEmbedLayers(layerIds).join(','));
target = url.toString();
} else {
target = `${origin}${EMBED_MAP_FRAME_PATH}?${publicFrameSearch(layerIds)}`;
}
const resp = await globalThis.fetch(target, {
method: 'GET',
headers,
credentials: 'omit',
});
if (resp.status === 429 || resp.status >= 500) {
throw new EmbedMapFrameUnavailableError(frameRetryAfterMs(resp));
}
if (!resp.ok) throw new Error(`Embed map frame request failed: ${resp.status}`);
return await resp.json() as EmbedMapFrameResponse;
}
/**
* The cacheable query for the free layers among `layerIds`, falling back to
* the plain uncacheable query when none qualify.
*
* Paid layers are dropped from the keyless URL rather than carried into it:
* a keyless caller cannot receive them either way, and naming them would
* multiply the shared key space past its seven free subsets. The frame marks
* them un-ready from their absence in the response, which is the same outcome
* an explicit `not-entitled` would produce.
*/
function publicFrameSearch(layerIds: readonly EmbedLayerId[]): string {
const free = getEmbedPanelFreeTier('map');
const eligible = free
? canonicalizeEmbedLayers(layerIds).filter((id) => free.layers.includes(id))
: [];View on GitHub (pinned to 7d06c8633d)