{"record":{"id":"3c509eed78bdda6e","repo":"benweet/stackedit","slug":"synchronization-failed-due-to-token-inconsistency","errorCode":null,"errorMessage":"Synchronization failed due to token inconsistency.","messagePattern":"Synchronization failed due to token inconsistency\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"src/services/syncSvc.js","lineNumber":631,"sourceCode":"};\n\n/**\n * Sync the whole workspace with the main provider and the current file explicit locations.\n */\nconst syncWorkspace = async (skipContents = false) => {\n  try {\n    const workspace = store.getters['workspace/currentWorkspace'];\n    const syncContext = new SyncContext();\n\n    // Store the sub in the DB since it's not safely stored in the token\n    const syncToken = store.getters['workspace/syncToken'];\n    const localSettings = store.getters['data/localSettings'];\n    if (!localSettings.syncSub) {\n      store.dispatch('data/patchLocalSettings', {\n        syncSub: syncToken.sub,\n      });\n    } else if (localSettings.syncSub !== syncToken.sub) {\n      throw new Error('Synchronization failed due to token inconsistency.');\n    }\n\n    const changes = await workspaceProvider.getChanges();\n\n    // Apply changes\n    applyChanges(workspaceProvider.prepareChanges(changes));\n    workspaceProvider.onChangesApplied();\n\n    // Prevent from sending items too long after changes have been retrieved\n    const ifNotTooLate = tooLateChecker(restartSyncAfter);\n\n    // Find and save one item to save\n    await utils.awaitSome(() => ifNotTooLate(async () => {\n      const storeItemMap = {\n        ...store.state.file.itemsById,\n        ...store.state.folder.itemsById,\n        ...store.state.syncLocation.itemsById,\n        ...store.state.publishLocation.itemsById,","sourceCodeStart":613,"sourceCodeEnd":649,"githubUrl":"https://github.com/benweet/stackedit/blob/6dce2a5e36b755a0c244522b48a06c91a2df0f59/src/services/syncSvc.js#L613-L649","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait store.dispatch('data/patchLocalSettings', { syncSub: syncToken.sub }); // silently skipped when stale\nawait sync();\n// after\nconst localSettings = store.getters['data/localSettings'];\nif (localSettings.syncSub && localSettings.syncSub !== syncToken.sub) {\n  await store.dispatch('data/patchLocalSettings', { syncSub: syncToken.sub }); // deliberate owner switch\n}\nawait sync();","handlingStrategy":"validation","validationCode":"const localSettings = store.getters['data/localSettings'];\nconst syncTokenSub = syncToken && syncToken.sub;\nif (localSettings.syncSub && syncTokenSub && localSettings.syncSub !== syncTokenSub) {\n  // resolve deliberately before syncing:\n  // reconnect with the original account, or confirm a switch and patch syncSub\n}","typeGuard":"function isSyncSubConsistent(localSettings, syncToken) {\n  return Boolean(syncToken && syncToken.sub && (!localSettings.syncSub || localSettings.syncSub === syncToken.sub));\n}","tryCatchPattern":"try {\n  await syncSvc.sync();\n} catch (err) {\n  if (err.message.includes('token inconsistency')) {\n    // surface an account-switch dialog instead of auto-overwriting syncSub\n  } else throw err;\n}","preventionTips":["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"],"tags":["sync","account-mismatch","local-settings","data-integrity"],"backgroundTag":"oauth-account-mismatch","analyzedSha":"6dce2a5e36b755a0c244522b48a06c91a2df0f59","analyzedAt":"2026-09-01T00:49:23.866Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}