benweet/stackedit · critical · Error
Synchronization failed due to token inconsistency.
Error message
Synchronization failed due to token inconsistency.
What it means
During synchronization the app keeps a localSettings.syncSub identifying which account sub owns the sync data. If a sync token's sub differs from the previously stored syncSub, syncSvc throws 'Synchronization failed due to token inconsistency.' to stop one account from writing into another account's synced data.
Solutions
- Reconnect the sync location with the account whose sub matches localSettings.syncSub.
- Clear/patch localSettings.syncSub (or reset local settings) so the next sync binds to the current account, accepting that the data owner changes.
- Verify which account the workspace data actually belongs to before overriding syncSub to avoid cross-account contamination.
- If the account was permanently switched, migrate/export the workspace data and start a fresh sync location.
Example fix
// before
await store.dispatch('data/patchLocalSettings', { syncSub: syncToken.sub }); // silently skipped when stale
await sync();
// after
const localSettings = store.getters['data/localSettings'];
if (localSettings.syncSub && localSettings.syncSub !== syncToken.sub) {
await store.dispatch('data/patchLocalSettings', { syncSub: syncToken.sub }); // deliberate owner switch
}
await sync(); Defensive patterns
Strategy: validation
Validate before calling
const localSettings = store.getters['data/localSettings'];
const syncTokenSub = syncToken && syncToken.sub;
if (localSettings.syncSub && syncTokenSub && localSettings.syncSub !== syncTokenSub) {
// resolve deliberately before syncing:
// reconnect with the original account, or confirm a switch and patch syncSub
} Type guard
function isSyncSubConsistent(localSettings, syncToken) {
return Boolean(syncToken && syncToken.sub && (!localSettings.syncSub || localSettings.syncSub === syncToken.sub));
} Try / catch
try {
await syncSvc.sync();
} catch (err) {
if (err.message.includes('token inconsistency')) {
// surface an account-switch dialog instead of auto-overwriting syncSub
} else throw err;
} Prevention
- Always check localSettings.syncSub against the current token before starting sync
- Make account switches an explicit user action that updates syncSub intentionally
- Back up workspace data before overriding syncSub to a new owner
- Avoid sharing app profiles between accounts on the same machine
When it happens
Trigger: Running sync (syncSvc) when the current workspace provider's sync token belongs to a different account than localSettings.syncSub — e.g. switching main/secondary accounts, restoring data from another account, or a stale localSettings entry after an account change.
Common situations: Reconnecting the sync location with a different Google/GitLab account; importing a workspace export from another user; localSettings surviving an account switch and pointing at the old sub; team setup where two users share a machine profile.
Related errors
- Dropbox account ID not expected.
- GitHub account ID not expected.
- GitLab account ID not expected.
- Google account ID not expected.
- WordPress account ID not expected.
AI-assisted analysis of benweet/stackedit@6dce2a5e36 (2026-09-01).
Data as JSON: /api/errors/3c509eed78bdda6e.
Report an issue: GitHub.
Appendix: source
Thrown at src/services/syncSvc.js:631
};
/**
* Sync the whole workspace with the main provider and the current file explicit locations.
*/
const syncWorkspace = async (skipContents = false) => {
try {
const workspace = store.getters['workspace/currentWorkspace'];
const syncContext = new SyncContext();
// Store the sub in the DB since it's not safely stored in the token
const syncToken = store.getters['workspace/syncToken'];
const localSettings = store.getters['data/localSettings'];
if (!localSettings.syncSub) {
store.dispatch('data/patchLocalSettings', {
syncSub: syncToken.sub,
});
} else if (localSettings.syncSub !== syncToken.sub) {
throw new Error('Synchronization failed due to token inconsistency.');
}
const changes = await workspaceProvider.getChanges();
// Apply changes
applyChanges(workspaceProvider.prepareChanges(changes));
workspaceProvider.onChangesApplied();
// Prevent from sending items too long after changes have been retrieved
const ifNotTooLate = tooLateChecker(restartSyncAfter);
// Find and save one item to save
await utils.awaitSome(() => ifNotTooLate(async () => {
const storeItemMap = {
...store.state.file.itemsById,
...store.state.folder.itemsById,
...store.state.syncLocation.itemsById,
...store.state.publishLocation.itemsById,View on GitHub (pinned to 6dce2a5e36)