{"record":{"id":"1b7ab58bea556692","repo":"gitbutlerapp/gitbutler","slug":"invalid-token-format","errorCode":null,"errorMessage":"Invalid token format","messagePattern":"Invalid token format","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/shared/src/lib/login/loginService.ts","lineNumber":152,"sourceCode":"\t\t\t},\n\t\t\t(data) => {\n\t\t\t\tif (!isStr(data.message)) throw new Error(\"Invalid message format\");\n\t\t\t\tif (!isStr(data.token)) throw new Error(\"Invalid token format\");\n\t\t\t\treturn { message: data.message, token: data.token };\n\t\t\t},\n\t\t);\n\t}\n\n\tasync resetPassword(email: string): Promise<LoginResponse<{ message: string }>> {\n\t\treturn await this.sendPostRequest(\"sessions/forgot_password\", { email }, (data) => {\n\t\t\tif (!isStr(data.message)) throw new Error(\"Invalid message format\");\n\t\t\treturn { message: data.message };\n\t\t});\n\t}\n\n\tasync loginWithEmail(email: string, password: string): Promise<LoginResponse<string>> {\n\t\treturn await this.sendPostRequest(\"sessions/login_with_email\", { email, password }, (data) => {\n\t\t\tif (!isStr(data.token)) throw new Error(\"Invalid token format\");\n\t\t\treturn data.token;\n\t\t});\n\t}\n\n\tasync resendConfirmationEmail(email: string): Promise<LoginResponse<{ message: string }>> {\n\t\treturn await this.sendPostRequest(\"sessions/resend_confirmation\", { email }, (data) => {\n\t\t\tif (!isStr(data.message)) throw new Error(\"Invalid message format\");\n\t\t\treturn { message: data.message };\n\t\t});\n\t}\n\n\tasync token(): Promise<LoginResponse<string>> {\n\t\treturn await this.sendGetRequest(\"sessions/toke_me_bro\", (data) => {\n\t\t\tif (!isStr(data.token)) throw new Error(\"Invalid token format\");\n\t\t\treturn data.token;\n\t\t});\n\t}\n","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/2497b8007aa4a1922dae9a805b32ffe5b5037785/packages/shared/src/lib/login/loginService.ts#L134-L170","documentation":"Thrown in the success handler of loginWithEmail (loginService.ts:152): POST sessions/login_with_email returned response.ok but the JSON body has no string token. Login cannot proceed without a session token, so this is fatal for the sign-in flow. sendPostRequest converts it to {type:\"error\", errorCode:\"network_error\", errorMessage:\"Invalid token format\"}.","triggerScenarios":"POST sessions/login_with_email returns 200 with the token under a different key (session_token, access_token), as an empty object (e.g. an MFA challenge returned with 200), or as parsed HTML from a misrouted host or gateway.","commonSituations":"Backend version renames or regroups the token field; a 2FA-required response delivered as 200; base URL pointing at a gateway; test doubles returning a fixed JSON shape that drifted from the real API.","solutions":["Log the actual sessions/login_with_email 200 body to find where the token lives","Verify the credentials flow completes without an MFA challenge, or handle the challenge response first","Align app and backend versions on the {token: string} success contract","Fix publicApiBaseUrl if it is not the API origin"],"exampleFix":"// before\nif (!isStr(data.token)) throw new Error(\"Invalid token format\");\nreturn data.token;\n\n// after — locate the token explicitly and fail with a diagnosable message\nconst token = isStr(data.token) ? data.token : undefined;\nif (!token) throw new Error(`Invalid token format (keys received: ${Object.keys(data).join(\", \")})`);\nreturn token;","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function isLoginSuccess<T>(res: LoginResponse<T>): res is { type: \"success\"; data: T } {\n\treturn res.type === \"success\";\n}","tryCatchPattern":"const res = await loginService.loginWithEmail(email, password);\nif (res.type === \"error\") {\n\t// \"Invalid token format\" means the server returned 2xx without a token — inspect raw payload in devtools\n\treportLoginIssue(res.errorCode, res.errorMessage);\n} else {\n\tsession.setToken(res.data); // data is the token string\n}","preventionTips":["Complete any MFA step before expecting a token in the response","Contract-test sessions/login_with_email returns {token: string}","Store the token immediately; re-request via token() only after a completed login"],"tags":["login","authentication","schema-validation","typescript"],"backgroundTag":"response-schema-validation","analyzedSha":"2497b8007aa4a1922dae9a805b32ffe5b5037785","analyzedAt":"2026-08-17T00:30:25.648Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}