When TON Connect breaks, the failure surfaces in one of two places — an explicit error code from the wallet, or a manifest fetch problem before the wallet even sees the request. This page collects each. For frequently asked questions about expected behaviour rather than failures, see the FAQ.Documentation Index
Fetch the complete documentation index at: https://companyname-a7d5b98e-feature-fumodocs.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Common errors
MANIFEST_NOT_FOUND_ERROR — code 2
The wallet could not fetch tonconnect-manifest.json at the URL the dApp passed in ConnectRequest.manifestUrl. Surfaces only as a connect_error event (connect event codes).
Causes:
- The manifest URL returns a non-2xx status — wrong path, wrong host, or behind authentication.
- The dApp is hosted on
localhostor a private network the wallet cannot reach. - A WAF or CORS rule blocks unauthenticated
GETrequests from the wallet.
curl -i https://yourapp.com/tonconnect-manifest.json.
MANIFEST_CONTENT_ERROR — code 3
The wallet fetched the manifest but rejected the body — JSON parse failure or a missing required field (url, name, iconUrl). An unreachable icon alone should not trigger this code — the spec lets wallets substitute a placeholder rather than blocking connect on icon failure. Surfaces only as a connect_error event (connect event codes).
Action. See Manifest 404 and CORS. Validate the JSON against the Manifest JSON schema and drop any trailing slash from url.
Bridge unreachable or timeout
Not a protocol error code — surfaces as a network failure, a closedEventSource, or a hung promise on the dApp side.
Causes:
- The wallet’s HTTP bridge is down or rate-limiting the dApp’s
client_id. - The user’s network blocks the bridge domain.
- The wallet never reconnected its SSE channel after backgrounding.
POST /message every 5 seconds. The resend loop runs indefinitely unless the dApp passes an explicit attempts count or an AbortSignal to the request — set one if you need a give-up policy. Surface a Connection problem state in the UI and offer to reconnect. Each wallet entry in wallets-v2.json lists at most one SSE bridge URL — there is no fallback inside the entry, so a permanently broken bridge has to be fixed by the wallet provider.
USER_REJECTS_ERROR — code 300
The user tapped Cancel in the wallet, or closed the wallet before signing. Surfaces on sendTransaction, signMessage, signData, and as a connect_error event. It does not surface on disconnect — that method has no user-cancel path (disconnect RPC specification).
Action. Treat this as the user changed their mind path. Show a soft message (“Transaction cancelled”) and let them retry. Do not log it as a system error.
BAD_REQUEST_ERROR — code 1
The wallet rejected the payload as malformed. Surfaces on every RPC method and in connect_error events. Common triggers:
- An address in raw format (
0:abc…) where the wallet expects TEP-2 friendly (base64url with the bounceable flag). - Both
messagesanditemspresent insendTransactionorsignMessage— the payload must contain one or the other, never both (RPC specification). - An entry in
itemswhosetypeis not in the wallet’s advertisedFeature.itemTypes('ton','jetton','nft'). valid_untilalready in the past at the moment the wallet receives the request.networkdoes not match the network currently selected in the wallet.
messages XOR items rule. If the request was assembled from user input, validate before sending.
METHOD_NOT_SUPPORTED — code 400
The wallet does not implement the requested method, or it does not advertise the feature the request relies on. The same code is reused per-item by connect item codes when the wallet cannot honour an individual ConnectItem such as ton_proof.
| Method or item | Required feature |
|---|---|
sendTransaction | SendTransaction |
signMessage | SignMessage |
signData (text) | SignData with 'text' in types |
signData (binary) | SignData with 'binary' in types |
signData (cell) | SignData with 'cell' in types |
Embedded request (e URL parameter) | EmbeddedRequest |
Structured items | SendTransaction or SignMessage with itemTypes covering each item |
ton_proof connect item | per-item code 400 in the ConnectItemReply |
walletsRequiredFeatures to hide wallets that lack the feature before the user reaches the connect modal.
UNKNOWN_APP_ERROR — code 100
The wallet does not recognise the app’s session. Surfaces on every RPC method, on connect_error, and on restoreConnection() when the wallet has cleared the session.
Causes:
- The session was previously revoked from the wallet.
- The app’s
client_iddoes not match what the wallet has stored for that session.
UNKNOWN_ERROR — code 0
A wallet-side fallback when no other code applies — internal exception, account locked, contract resolution failure, or a feature the SDK does not recognise.
Action. Inspect the message field on the bridge response first; the SDK surfaces it through the rejected promise and into your browser console. If message is empty or generic, check the wallet’s own logs — the bridge response will not have more detail than { code: 0, message }. Repeated code 0 from a single wallet is worth reporting upstream as a wallet-side bug.
Correlating logs with traceId
Every sendTransaction, signMessage, signData, and disconnect call attaches a traceId (UUID, UUIDv7 by default) to the bridge request, the connect URL, and the wallet’s reply. The same value comes back on result.traceId. Log it alongside your analytics and include it when reporting issues — the wallet, bridge, and dApp share one ID per user-visible operation, which is what makes a server-side log search tractable.
Manifest 404 and CORS
A manifest fetch error means the wallet could not load or validate yourtonconnect-manifest.json. Before showing the connect prompt, the wallet fetches this file over HTTPS from any origin without authentication. Common causes are an unreachable host, blocked CORS, an HTML gateway challenge, and an iconUrl that does not load. In these cases the wallet returns MANIFEST_NOT_FOUND_ERROR (code 2) or MANIFEST_CONTENT_ERROR (code 3).
Use curl to identify the failing layer. For the manifest schema and fields, see Manifest and the App manifest specification.
Self-diagnosis with curl
Run this request from a phone hotspot, VPS, or another context where your dApp cookies do not apply:
- HTTP
200status. The HTTP version on the status line is not relevant. Content-Type: application/json.Access-Control-Allow-Originset to*or to the requestOrigin.- A JSON body with
url,name, andiconUrl.
403, or no Access-Control-Allow-Origin, go to the matching section.
To exercise the cross-origin path explicitly, repeat the request with an arbitrary Origin header:
Access-Control-Allow-Origin value.
Manifest URL is unreachable
Symptom. The connect modal shows, but after the user taps a wallet, the wallet reports an error or does not open. Check. Open the manifest URL in an incognito browser tab. You should see raw JSON. Common causes.- The file is outside
public/(Next.js, Vite) or missing from the build output. - A CDN in front of your domain still serves an old build.
- The manifest lives on a subdomain (
api.example.com/...) whilemanifestUrlpoints to the apex domain.
curl -i https://example.com/tonconnect-manifest.json. Expect HTTP 200 status and a JSON content type. Avoid curl -I (HEAD). Some hosts emit different headers for HEAD than for the GET request wallets use.
CORS blocks the wallet
Symptom. The manifest URL is reachable from your dApp origin, but the wallet still reports a manifest error. Why. The wallet requests the manifest from another origin: a wallet web app, a native app, or an in-app webview. The App manifest specification requires the file to be reachable from any origin without CORS restrictions. Fix. Serve the manifest with permissive CORS:Cloudflare or WAF challenge
Symptom. The manifest loads in a browser, but the wallet sometimes fails to fetch it. Why. Cloudflare’s Bot Fight Mode, JavaScript challenge, or “Under Attack” mode can return an HTML challenge page instead of JSON. The wallet receives HTML, fails to parse it as JSON, and reportsMANIFEST_CONTENT_ERROR (code 3).
Fix. Bypass the challenge for the manifest path:
- In Cloudflare, add a Configuration Rule or legacy Page Rule that disables Bot Fight Mode for
/tonconnect-manifest.json. - In other gateways, add an equivalent allow rule for the same path. This includes Vercel Firewall, Akamai Bot Manager, and AWS WAF.
Auth proxy or iconUrl 404
Symptom. The manifest itself loads, but the wallet still reports a content error.
Why. The iconUrl in the manifest is unreachable, sits behind authentication, or returns 404. Some wallets refuse to display a manifest when its icon does not load and report MANIFEST_CONTENT_ERROR (code 3).
Fix. Test the icon URL directly with curl -i. The icon must be PNG or ICO. SVG is not supported. Use a 180×180 px PNG and host it on the same public path without authentication.
Caching pitfalls
The TON Connect protocol does not define manifest caching. Wallets cache at their own discretion, so behaviour is implementation-specific. After publishing a fix:- A wallet may keep using the previously cached broken manifest until its own TTL expires.
- Changing the manifest path to
tonconnect-manifest-v2.jsonand updatingmanifestUrlin the SDK constructor forces a fresh fetch.
See also
- FAQ — frequently asked questions about behaviour and design choices.
- Universal links —
retparameter reference. - Connect-and-act in one tap — reduces round-trips on mobile.
- RPC specification (
sendTransaction,signMessage,signData,disconnect) and Connect specification (connect event, connect item) — full normative error tables.