Skip to content

Troubleshooting

A graph stays waiting forever. No device that holds the key has synced since this one registered. Open the app on a device that has the key, or check autoGrant is not off. If a device holds keys but is not granting, look for error events with scope keys.gaps.

status.pending never reaches zero. Look for an error event. Scope keys.behind means this device lacks the current key — another device must sync. A repeated push error with retryable: false means the relay is refusing the batch; check record.rejected for the reason.

status.rejected is climbing. The relay refused records for good. record.rejected with failure.reason === 'relay-rejected' carries the relay’s reason: rev-too-far-ahead (a clock problem), graph-record-quota or graph-byte-quota (a full graph).

Records vanish on one device. Almost always a schema mismatch: that device’s KindRegistry rejects what another device wrote. Watch record.rejected with reason schema or unknown-kind. Consider onUnknown: 'accept' during rolling deploys.

deviceOk is false. This device id is bound to different public keys upstream — usually a keystore that was wiped while the device id survived (a cleared browser profile with the id still in localStorage). Mint a new device id; never retry the registration.

409 device-key-conflict on boot. The same thing from the relay’s side. New device id.

403 device-not-registered. Your authenticate returned a device id that is not registered to that member. Check the token binds the device the engine is actually using.

Two tabs disagree about unsynced writes. OPFS is one tab at a time and the second fell back to IndexedDB. Use IdbGraphStore everywhere, or run one engine in a SharedWorker.

A rotation returns 400 incomplete-rotation. Your Directory.membersOfGroup disagrees with the relay’s device roster — the rotation did not cover every active device of every current member. The response names the missing device ids.

Everything is slow on a big import. The per-record cost is dominated by ECDSA (~310µs to sign, ~300µs to verify). An app that genuinely does not need authorship can set sign: false and roughly halve both, at the cost of by becoming a claim again.