Troubleshooting and error reference
When an app misbehaves on the platform it is usually one of a few things: a missing readiness call, a surface that never reported its size, a handshake the shell never answered, or a payload that cannot cross the iframe boundary. This page collects those failure modes with their fixes, then lists every error code the SDK emits.
Common failure modes
Section titled “Common failure modes”Your app connects but receives no intents or events
Section titled “Your app connects but receives no intents or events”connect() resolved and the session looks right, but the handlers you registered with provide() and
subscribe() never fire.
The shell holds all intent, event, and navigation traffic for your app until you call
platform.ready(). Until that call nothing is delivered, so an app that forgets it looks connected
but stays inert. Wire your provide(), subscribe(), and route.onShellNavigate() handlers, then
call ready() once. See The readiness barrier.
An embedded surface never renders or never sizes
Section titled “An embedded surface never renders or never sizes”embedSurface() returned a handle, but the embedded iframe stays blank, or it renders clipped at zero
height. There are three causes, each visible in how the SDK handles a surface:
- The embed was refused or torn down.
embedSurface()resolves synchronously; the surface is only live once itsreadypromise resolves, and that promise rejects if the shell refuses the embed or tears it down. Attach.catchtohandle.ready(an unhandled rejection escapes otherwise) and reade.codeto see why. That code is the shell’s close reason, not one of the codes the SDK mints; see the error code reference for how to read it. - The surface never reported a height. The shell sizes the surface’s iframe from the height the
surface reports. Inside the surface body, call
conn.observe(rootElement)(orconn.reportHeight(px)) so the shell can track your content; without it the iframe can stay at zero. ready()was never called, or called too early. Callconn.ready()once the surface has rendered, and report height after layout so the first height the shell sees is the final one.
See Provide and host an embedded surface for the full provider and host wiring.
The handshake never completes
Section titled “The handshake never completes”connect() (or bootstrap()) neither resolves nor rejects for a while, then rejects with
[platform] handshake timed out waiting for welcome.
After your app posts its hello, the SDK waits for the shell’s welcome. If none arrives within ten
seconds it rejects with that error. The SDK accepts a welcome only when its origin matches the
platformShellOrigin stamped on your entry URL and its appId matches yours; a message from any other
origin, or for a different app, is ignored, so an origin or id mismatch also ends in this same timeout.
Confirm the shell is actually loading your app and that its origin matches the one on your entry URL.
This is a thrown Error, not a PlatformError with a code.
connect() throws immediately
Section titled “connect() throws immediately”connect() throws synchronously, before any handshake, in three cases:
- No
windowis present, for example during server-side rendering:[platform] connect() must run in a browser. - The app is running at the top level instead of inside the shell’s iframe:
[platform] connect() must run inside the platform shell iframe (no parent window found). - The entry URL does not carry the parameters the shell stamps on it:
[platform] missing handshake params on the app URL (platformShellOrigin, platformAppId).
In practice all three mean your app is running outside the shell. Let the shell load it, or, for local
development without a shell, pass a fallback to bootstrap() so the app comes up in its logged-out
standalone state instead of throwing. connectSurface() throws the equivalent errors for a surface
entrypoint.
A call throws before it sends anything
Section titled “A call throws before it sends anything”invoke(), publish(), notify.toast(), log(), embedSurface(), a modal open, or a surface
close() throws a SerializationError synchronously, before the call leaves your app.
Every payload that crosses the iframe boundary must be structured-clone serialisable. The SDK checks
each one eagerly and throws SerializationError rather than letting the browser raise an opaque
DataCloneError. Pass plain data only: no functions, DOM nodes, or class instances. The error’s
context names the offending call, for example invoke:customer.lookup.
Error code reference
Section titled “Error code reference”Every code below arrives on a PlatformError
({ code, message, details? }), the shape that invoke() rejects with and that a surface or service
call carries on failure. These are the codes the SDK itself mints.
| code | what it means | retryable? | what to do |
|---|---|---|---|
TIMEOUT | No response arrived within the call’s window: 30 seconds for invoke, notify, navigate, and flags; about 30 minutes for an open modal. | Yes; it is a transient failure. | Make sure the provider or service actually responds. For an invoke whose work is legitimately long, raise InvokeOptions.timeoutMs. |
NO_HANDLER | The provider that received your invoke has no handler registered for that exact intent@version. | Only if the provider had simply not registered yet; a name or version mismatch will not fix itself. | Check the provider calls provide(intent, version, ...) for the version you requested, and that it has called ready(). |
HANDLER_ERROR | The provider’s intent handler threw. message carries the provider’s own error text. | Depends on the provider’s failure, not on the SDK. | Read message; the fault is in the provider’s handler, not in your call. |
UNKNOWN | The call failed but no structured error came back, so the SDK substituted this, with the message Intent failed or Service failed. | Unknown; the failure carried no detail. | Treat it as an unclassified failure and check the provider and shell logs. |
See also
Section titled “See also”- The isolation model: the handshake and the readiness barrier.
- Provide and host an embedded surface: surface lifecycle and sizing.
PlatformError: the shape every rejected SDK call carries.