Errors, limits and versioning
Errors
A command that fails rejects with an EmbedError. Its code is stable and meant to be branched on; message is written for a developer reading a console.
const { EmbedError } = IconMapEmbed;
try {
await map.applyBookmark({ bookmarkId });
} catch (e) {
if (!(e instanceof EmbedError)) throw e;
switch (e.code) {
case "unknown_id": return forgetBookmark(bookmarkId);
case "denied_capability": return hideBookmarkMenu();
default: report(e.code, e.message, e.requestId);
}
}
| Property | |
|---|---|
code |
One of the error codes |
message |
What went wrong, in a sentence |
detailedMessage |
The underlying cause, when there is one |
requestId |
Present when the failure involved our servers - quote it in a support request |
detail |
Structured extras, for example { undeclared: [...] } on undeclared_filter |
Which code means what
The codes fall into four families, and the right response differs for each.
Your page is not allowed - denied_origin, denied_capability. The author decides these. denied_origin rejects ready itself: no session opened, and nothing will work until your site is added to the map's allowed origins. denied_capability means one group is off (or, for exportImage and print, that a sensitivity label refuses the export). Hide the feature; retrying cannot help.
The map does not have it - unsupported_command, unknown_id, undeclared_filter. Check capabilities.commands before offering a feature, and build pickers from what the map reports (getLayers, getBookmarks, getFilterSchema) rather than from ids in your code.
The call was wrong - invalid_args, too_large. A bug in the calling code; the message names the argument.
Try again later - rate_limited, busy, budget_exceeded, data_error, timeout, not_ready. Back off rather than loop.
Two codes come from the SDK rather than the map: timeout (no answer within 15 seconds; change with the commandTimeoutMs option) and destroyed (you destroyed or reloaded the map while a command was in flight).
Start-up failures
If the map cannot start at all - a revoked publish, a token refused - ready rejects and the onError option is called. The map shows its own message to the reader unless you pass hideErrors: true and show yours.
Limits
These are fixed. Your page cannot raise them and an author cannot lower them; they are also exported as EMBED_LIMITS.
| Limit | Value | When exceeded |
|---|---|---|
| Commands | 60 a second | rate_limited |
| One command message | 64 KB | too_large, and the command is never read |
| One event payload | 32 KB | the event is dropped |
Keys in highlight / selectByKey |
500 | invalid_args |
| Analysis operations | 60 a minute, 1 running + 4 waiting per layer | budget_exceeded, busy |
| Analysis results | 1,000 ids or 200 rows per list | cut, with truncated: true |
featureHover |
one per 100 ms | coalesced - the last value always arrives |
Capabilities, not versions
await map.ready resolves with a descriptor of what this map will answer:
{
protocol: 2,
commands: ["getView", "setView", ...],
events: ["viewChanged", "featureClick", ...],
policy: { enabled: true, read: true, camera: true, layers: true, ... },
map: { publishId, surface: "published", dataMode: "snapshot", bookmarks: true, locales: ["en-US", "de-DE", ...] }
}
commands is already the intersection of three things: what this version of the map implements, what this kind of embed supports, and what the author switched on. So the only check you ever need is:
if (capabilities.commands.includes("exportImage")) showExportButton();
New commands arrive by being added to that list. A page written today keeps working; a page that wants a newer command asks for it and degrades when it is absent.
Compatibility between old and new
The map and the SDK are released separately, so each copes with the other being older.
| Your page uses | The map is | What happens |
|---|---|---|
| Current SDK | Current | Everything in this documentation |
| Current SDK | An older map without the control API | The map displays. ready resolves with an empty commands list rather than hanging; commands reject unsupported_command |
SDK 0.x (display and setFilters only) |
Current | Works unchanged |
The version agreed is in map.protocol. You should not need it.
Lifecycle
map.reload(); // reload the frame; subscriptions are restored when it is back
map.destroy(); // remove the frame; anything in flight rejects with `destroyed`
getEmbed(element) returns the handle already mounted in an element, and reset(element) destroys it - useful in frameworks where the code that mounts the map is not the code that cleans up.
Security model
For a security review, the properties that matter:
- Isolation. The map runs in a cross-origin iframe. It cannot read your page, and your page cannot read it; the only channel is
postMessage. - Who may speak. The map accepts messages only from the window that framed it, and only if that window's origin is on the author's allowed origins. A third-party frame on your page cannot drive the map, even from an allowed origin. The SDK in turn accepts messages only from the frame it created, on the origin it expects, and sends tokens nowhere else.
- What may be said. A closed list of commands with validated arguments. None takes a URL, a query, a style, HTML or script.
- What comes back. Only what a reader can already see: tooltip fields, never whole rows; never the map's configuration, data sources or queries.
- Who decides. Allowed origins and the command-group switches are part of the signed publish, enforced inside the map - and, where a command reaches our servers (
refreshData), enforced there as well. - Data access is unchanged. The API adds no new route to data. A published map reads what the publish froze or what its token's row-level security allows; an organizational embed reads as the signed-in viewer.
- Sensitivity labels. Image export and print are decided by the same sensitivity-label rules as every other export from Icon Map.
- Telemetry. We count control sessions and which command groups are used. Argument values - filter values, selections, your menu titles - are never collected.
The same allowed-origins list also sets the frame-ancestors policy, so it governs which sites can frame the map at all.