{"record":{"id":"df7fc97cbc08bf63","repo":"benweet/stackedit","slug":"gitlab-account-id-not-expected","errorCode":null,"errorMessage":"GitLab account ID not expected.","messagePattern":"GitLab account ID not expected\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/services/providers/helpers/gitlabHelper.js","lineNumber":78,"sourceCode":"        scope: 'api',\n      },\n      silent,\n    );\n\n    // Call the user info endpoint\n    const user = await request({ accessToken, serverUrl }, {\n      url: 'user',\n    });\n    const uniqueSub = `${serverUrl}/${user.id}`;\n    userSvc.addUserInfo({\n      id: `${subPrefix}:${uniqueSub}`,\n      name: user.username,\n      imageUrl: user.avatar_url || '',\n    });\n\n    // Check the returned sub consistency\n    if (sub && uniqueSub !== sub) {\n      throw new Error('GitLab account ID not expected.');\n    }\n\n    // Build token object including scopes and sub\n    const token = {\n      accessToken,\n      name: user.username,\n      serverUrl,\n      sub: uniqueSub,\n    };\n\n    // Add token to gitlab tokens\n    store.dispatch('data/addGitlabToken', token);\n    return token;\n  },\n  async addAccount(serverUrl, applicationId, sub = null) {\n    const token = await this.startOauth2(serverUrl, applicationId, sub);\n    badgeSvc.addBadge('addGitLabAccount');\n    return token;","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/benweet/stackedit/blob/6dce2a5e36b755a0c244522b48a06c91a2df0f59/src/services/providers/helpers/gitlabHelper.js#L60-L96","documentation":"startOauth2 completes the GitLab OAuth2 flow: it exchanges the code for tokens, loads /user, and builds a unique sub (accountId-based identity string). If a previously stored 'sub' was supplied and the freshly returned identity does not match it, the helper throws 'GitLab account ID not expected.' to prevent attaching a different GitLab account's token to an existing entry.","triggerScenarios":"OAuth callback (token) or token refresh when the stored sub differs from the newly authenticated GitLab user's identity string — e.g. the user logged into a different GitLab account in the browser during the flow, or the stored sub is stale/corrupted.","commonSituations":"Multiple GitLab accounts (personal + work) in one browser session; self-hosted GitLab instance migrated users to new IDs; clearing app data while the OAuth session cookie still points at another account; subdomain/instance changed so the unique sub format no longer matches.","solutions":["Log out of GitLab in the browser (or use a private window) and reconnect with the intended account.","Clear the stored GitLab token/sub for this account in the app's data store, then re-run the OAuth flow to bind a fresh sub.","Verify the GitLab instance URL/unique-sub construction matches what was stored previously.","If refreshing, delete the stale refresh token so a full sign-in re-establishes the correct sub."],"exampleFix":"// before\nconst token = await gitlabHelper.startOauth2(code, storedSub);\n// after\ntry {\n  const token = await gitlabHelper.startOauth2(code, storedSub);\n} catch (e) {\n  if (e.message === 'GitLab account ID not expected.') {\n    // stored sub belongs to another account; reconnect with no expected sub\n    const token = await gitlabHelper.startOauth2(code, undefined);\n  } else throw e;\n}","handlingStrategy":"try-catch","validationCode":"// before starting OAuth, confirm the browser session account matches the stored identity if possible\nconst stored = store.getters['data/gitlabTokensBySub'][expectedSub];\nif (!stored) expectedSub = undefined; // no stale expectation to violate","typeGuard":"function isExpectedGitlabSub(uniqueSub, expectedSub) {\n  return !expectedSub || uniqueSub === expectedSub;\n}","tryCatchPattern":"try {\n  const token = await gitlabHelper.startOauth2(code, expectedSub);\n} catch (err) {\n  if (err.message === 'GitLab account ID not expected.') {\n    // clear stale token entry and re-run OAuth without expectedSub\n  } else throw err;\n}","preventionTips":["Use login_hint / explicit account selection when starting the OAuth flow","Clear stored tokens/subs whenever the linked account changes or is removed","Never reuse stored subs copied between environments or databases","For self-hosted GitLab, re-verify subs after instance migrations"],"tags":["gitlab","oauth2","account-mismatch","token"],"backgroundTag":"oauth-account-mismatch","analyzedSha":"6dce2a5e36b755a0c244522b48a06c91a2df0f59","analyzedAt":"2026-09-01T00:49:23.866Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}