Troubleshooting the SDK
The errors the SDK, the Nuxt module and the CLI raise, what each one means, and the fix.
On this page
This page is for anyone staring at an error from @dukkan.one/app-sdk, @dukkan.one/nuxt or dukkan app. Every entry names the cause and the fix; where a portal page owns the fix, it is linked.
Errors the SDK throws#
Every error extends DukkanError and carries a stable code; API errors add status, requestId and details from the platform's error envelope.
| Error | Meaning | Fix |
|---|---|---|
DukkanAuthError with reason: "invalid_token" |
The access token was refused. The SDK already refreshed once and retried; this means the refresh failed too. | Check DukkanReconnectRequiredError below; otherwise the clock of your server is off by more than the token lifetime allows. |
DukkanAuthError with reason: "access_removed" |
The granting member lost access to the store. | Send the merchant through consent again (Suspension). |
DukkanAuthError with reason: "no_permissions" |
The install holds no effective scopes any more. | Same: re-consent. |
DukkanReconnectRequiredError |
The store has no tokens for the install, or a refresh answered invalid_grant and the row was marked. |
Show your "reconnect" screen; the merchant installs again. Never retry the refresh in a loop. |
ScopeLostError |
403: the call or the topic needs a scope the install does not hold. topics names the refused topics. |
Request the scope in dukkan.app.toml and re-consent, or drop the call. |
DukkanRateLimitError |
429 after the SDK's retry budget. retryAfterSeconds is the server's number. |
Slow down; the limits are in the API reference. |
IdempotencyConflictError |
The same Idempotency-Key was sent with a different payload, or is still in progress (inProgress). |
Derive the key from the write's identity (idempotency: { ref }), never from the attempt. |
InvalidTransitionError |
The order cannot move from its current status to the requested one. details.current_status says where it is. |
Read the status matrix; fetch the order and decide again. |
DukkanWebhookError (constructEvent only) |
The delivery was refused: bad_signature, stale_timestamp, missing_headers, malformed_envelope, store_mismatch, body_too_large. |
See the receiver section below. |
DukkanConfigError |
dukkan.app.toml failed validation; the message lists every problem. |
The dukkan.app.toml file. |
DukkanNetworkError |
The platform could not be reached or timed out (15 seconds). | Retry later; the SDK already retried idempotent calls. |
The webhook receiver answers a refusal#
| Body | Status | Cause | Fix |
|---|---|---|---|
bad_signature |
401 |
The secret is wrong, or the body was re-serialised before verification. | Load the secret the platform minted for this install; pass the raw request bytes. |
stale_timestamp |
400 |
The delivery is older than 5 minutes, or your clock is off. | Sync the server clock; a genuinely late delivery is retried by the platform with a fresh signature. |
missing_headers |
400 |
No X-Dukkan-Timestamp or X-Dukkan-Hmac-Sha256. |
A proxy strips custom headers; forward them. |
unknown_install |
404 |
secretsFor returned null, or the signed install_id differs from the header hint. |
Persist the secret at install time (webhookSecrets.save); the platform retries until you do. |
store_mismatch |
403 |
The envelope's store_id is not the store you bound the install to. |
Never happens for a correctly stored install; investigate your binding. |
body_too_large |
413 |
Over 256 KB. | The platform never sends that; check for a proxy that wraps bodies. |
malformed_envelope |
400 |
Verified bytes that are not a v1 envelope. | Report it with the X-Request-Id; this is a platform bug. |
A { "ok": true, "duplicate": true } answer means the inbox already held the event id: the platform retried a delivery you had acknowledged. Nothing to fix.
The Nuxt module#
| Message | Fix |
|---|---|
Dukkan app is not configured: NUXT_DUKKAN_CLIENT_SECRET is not set (503, or a refused boot in production) |
Set the variable; the full list is in Environment. |
sessionSecret must be at least 32 characters |
Generate one: openssl rand -base64 48. |
dukkan.app.toml not found at build time |
Run dukkan app init, or point the module's configFile option at the file. |
server/dukkan.ts not found |
Create it with defineDukkanApp; see Your side of the contract. |
oauth_state_mismatch on the callback |
The state cookie expired (10 minutes), the callback arrived on a different host than the install request, or cookies are blocked. Check NUXT_DUKKAN_APP_BASE_URL matches the host merchants use. |
store_not_allowed on the callback |
A public tunnel is up and NUXT_DUKKAN_ALLOWED_STORE_IDS restricts installs to your sandbox. Expected while developing. |
token_exchange_failed:invalid_grant |
The code was used twice or the redirect URI differs from the registered one byte for byte. |
The CLI#
| Message | Fix |
|---|---|
✗ Not signed in. Run: dukkan login |
Sign in; credentials are bound to one sandbox store. |
✗ The app is not running on port 3000 with dev routes |
Start it with dukkan app dev; rehearsals need the dev routes, which exist only in nuxt dev. |
✗ The portal draft changed since your last pull |
Pull, or push with --force; see Drift. |
✗ This draft is under marketplace review and frozen |
Withdraw the submission in the portal first. |
✗ clients:read needs the accepted data processing agreement |
Accept it on the portal's Account page (Scopes). |
✗ The platform refused the rehearsal |
Sandbox stores only; the subscription must include the topic; the sandbox needs seeded data (Sandbox stores). |
cloudflared not found |
Install it, or pass --tunnel-url with any HTTPS tunnel, or run --no-tunnel and use webhook trigger --local. |
Every CLI command accepts --json for one JSON object per line when you need to script around it. The full command list is in the CLI reference.