Troubleshooting
When something fails, start by locating the layer that failed:- Pairing and trust establishment.
- Transport reachability.
- Capability authorization.
- Storage and key handling.
- App-specific RPC logic.
Pairing issues
Pairing code expired
Symptom:PAIRING_CODE_EXPIRED
Cause: Pairing codes have a 2-minute TTL and are single-use.
Fix:
- Generate a new pairing code.
- Complete pairing within 2 minutes.
- If you are testing a slow flow, shorten the path between scanning the QR and approving the device.
Pairing code already used
Symptom:PAIRING_CODE_USED
Cause: The code was consumed by another client or by a previous attempt.
Fix:
- Generate a fresh code.
- Do not reuse a QR after the host has already completed pairing.
- If the client auto-retried, clear the stale state before trying again.
Fingerprint mismatch
Symptom:FINGERPRINT_MISMATCH or “host fingerprint does not match”
Cause: The host fingerprint in the QR does not match the host the client actually reached.
Security warning: Treat this as a potential MITM or stale-QR event until proven otherwise.
Fix:
- Verify that the desktop host is the one you intended to pair with.
- Regenerate the QR from the current host process.
- Check whether a signaling or tunnel endpoint was swapped out underneath you.
Pairing rejected by host
Symptom:PAIRING_FAILED
Cause: The host’s approval callback returned false, timed out, or was never surfaced to the user.
Fix:
- Check the host logs for the rejection reason.
- Confirm the requested capabilities are allowed by policy.
- Make sure your approval UI is actually reaching the human who needs to answer.
Connection issues
Connection refused
Symptom:CONNECTION_FAILED
Fix:
-
Check the services that should be running:
- Confirm that the host and client are using the same signaling and relay URLs.
- Confirm that the host is binding to the right interface for the mode you selected.
- Check whether a firewall or another local process already owns the port.
Connection timeout
Symptom:CONNECTION_TIMEOUT
Fix:
- Check basic network connectivity first.
- Increase
dialTimeoutMsif the route is legitimately slow. - Confirm the route kind is valid for the network you are on.
- If a relay is configured, verify that the relay is reachable and not just the signaling service.
Relay unavailable
Symptom:RELAY_UNAVAILABLE
Fix:
- Check the relay URL and port.
- Check whether the relay service is actually running.
- Confirm the relay token and any client token are correct.
- If the devices are on the same network, try LAN first to isolate the issue.
Signaling unavailable
Symptom:SIGNALING_UNAVAILABLE
Fix:
- Check the signaling URL and port.
- Confirm the signaling service is running.
- Verify that the WebSocket upgrade is reaching the service.
- If you are using per-machine dev tokens, make sure the host and service are reading the same
.crosslink-datalocation.
RPC issues
Method not found
Symptom:METHOD_NOT_FOUND
Fix:
- Check the method name.
- Confirm the host exposes that method.
- Confirm the client is connected to the intended host, not a stale tab or old session.
Capability denied
Symptom:CAPABILITY_DENIED
Fix:
- Check whether the capability was requested during pairing.
- Check whether the host granted it.
- Re-pair with the required capability if the policy was too strict.
- If the capability should have been allowed, inspect the policy and approval hook on the host.
RPC timeout
Symptom:TIMEOUT
Fix:
- Check whether the host is responsive.
- Look for a slow handler or a queueing bug.
- Increase the timeout only after confirming the handler is actually healthy.
Storage issues
Secret storage unavailable
Symptom:STORAGE_ERROR
Fix:
- Check OS keychain availability first.
- If you are using Electron, confirm
safeStorageis available. - If you deliberately allow it, set
CROSSLINK_SECRET_KEYso the encrypted-file fallback has a fixed passphrase. - Use
allowPlaintextFallback: trueonly for development and only when you understand the risk.
Browser storage not encrypted
Symptom: Warning such as “identity seed is stored in the clear” Cause: WebCrypto or IndexedDB was unavailable, or the page is not in a secure context. Fix:- Serve the page from HTTPS or localhost.
- Confirm the browser actually supports the needed APIs.
- Prefer
CrosslinkClient.create()over the plain constructor when you need the secure storage path.
Transport issues
WebRTC not connecting
Symptom: The connection falls back to relay. Fix:- Check ICE configuration and any STUN/TURN dependencies.
- Confirm the direct path is actually available between the peers.
- Treat relay fallback as a valid outcome unless you specifically require a direct DataChannel.
LAN not working
Symptom: Devices cannot discover or reach one another on the local network. Fix:- Confirm both devices are on the same network segment.
- Check the host’s LAN listener port and firewall rules.
- Confirm the host is not bound only to loopback if the phone needs to reach it.
- If you are testing on one machine, loopback is fine; if you are testing across devices, it is not.
Remote access not working
Symptom:networkMode: "remote" fails startup or the QR does not contain a public route.
Fix:
- Run
npm run check:remoteornode scripts/check-remote-access.mjs <port>. - Confirm the router actually supports PCP, NAT-PMP, or UPnP IGD.
- Check for carrier-grade NAT or a double-NAT setup.
- If you configured a manual port forward, make sure the public host and external port match reality.
Debug logging
Use structured logs to debug, not ad-hoc string concatenation. The logger must support the full interface from@crosslink/core.
- Start with the event name and the transport or capability involved.
- Look for redacted reasons, not raw request objects.
- Do not paste secrets, tokens, seeds, or private keys into logs just to “see what is happening”.
- If the logger is too quiet, raise the level before you add more instrumentation.
Practical recovery loop
If you are stuck, use this sequence:- Restart the host and re-check the advertised endpoints.
- Regenerate the pairing QR.
- Verify the SAS digits on both devices.
- If the problem is network reachability, run the remote-access check.
- If the problem is authorization, inspect the capability policy and approval hook.
- If the problem is storage, verify the keychain or encrypted-file backend before looking elsewhere.