{"record":{"id":"3d6926241e5272d5","repo":"gitbutlerapp/gitbutler","slug":"invalid-message-format","errorCode":null,"errorMessage":"Invalid message format","messagePattern":"Invalid message format","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/shared/src/lib/login/loginService.ts","lineNumber":117,"sourceCode":"\t\t\t\terrorCode: \"unknown_error\",\n\t\t\t\terrorMessage: \"An unknown error occurred\",\n\t\t\t\traw: error,\n\t\t\t};\n\t\t}\n\t}\n\n\tasync finalizeAccount(\n\t\temail: string,\n\t\tusername: string,\n\t): Promise<LoginResponse<{ message: string }>> {\n\t\treturn await this.sendPostRequest(\n\t\t\t\"sessions/finalize\",\n\t\t\t{\n\t\t\t\temail,\n\t\t\t\tlogin: username,\n\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\treturn { message: data.message };\n\t\t\t},\n\t\t);\n\t}\n\n\tasync confirmPasswordReset(\n\t\ttoken: string,\n\t\tnewPassword: string,\n\t\tpasswordConfirmation: string,\n\t): Promise<LoginResponse<{ message: string; token: string }>> {\n\t\treturn await this.sendPostRequest(\n\t\t\t\"sessions/confirm_new_password\",\n\t\t\t{\n\t\t\t\tpassword_reset_token: token,\n\t\t\t\tpassword: newPassword,\n\t\t\t\tpassword_confirmation: passwordConfirmation,\n\t\t\t},\n\t\t\t(data) => {","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/packages/shared/src/lib/login/loginService.ts#L99-L135","documentation":"LoginService.finalizeAccount validates the JSON body returned by POST sessions/finalize before surfacing it as a success. If the response's `message` field is missing or not a string, it throws 'Invalid message format'. This is a server contract check: a 2xx response without the expected shape is treated as a failure rather than propagated blindly.","triggerScenarios":"The backend returns HTTP 200 for POST sessions/finalize with a JSON body that lacks a string `message` field (e.g. `{}` or `{message: 123}`), causing the successHandler's isStr(data.message) check to throw.","commonSituations":"Backend API changed and no longer returns `message` on success; a proxy/gateway returns an unexpected 2xx body (empty object, HTML converted to JSON); hitting a mock or wrong environment whose finalize endpoint returns a different shape; version mismatch between desktop app and backend.","solutions":["Inspect the actual 2xx response body of POST sessions/finalize (network tab) and confirm whether `message` is present and a string","Verify you are talking to the expected backend environment and API version","If the server is at fault, fix the endpoint to include a string `message` on success; if the client is stale, update the app","If the server legitimately omits `message`, relax the validation to tolerate a missing message and use a default"],"exampleFix":"// before\n(data) => {\n  if (!isStr(data.message)) throw new Error(\"Invalid message format\");\n  return { message: data.message };\n}\n// after\n(data) => ({ message: isStr(data.message) ? data.message : \"Account finalized\" })","handlingStrategy":"type-guard","validationCode":"const res = await fetch(base + \"sessions/finalize\", { method: \"POST\", ... });\nconst data = await res.json();\nif (res.ok && typeof data?.message !== \"string\") {\n  console.warn(\"finalize returned unexpected shape\", data);\n}","typeGuard":"function hasMessage(data: unknown): data is { message: string } {\n  return typeof data === \"object\" && data !== null && typeof (data as any).message === \"string\";\n}","tryCatchPattern":"const result = await loginService.finalizeAccount(email, username);\nif (result.type === \"error\") {\n  if (result.errorMessage === \"Invalid message format\") {\n    // 2xx body did not match the expected contract; log raw for diagnosis\n    console.error(\"finalize: unexpected response shape\", result.raw);\n  }\n  showError(result.errorMessage);\n  return;\n}","preventionTips":["Version-pin and smoke-test the backend login API contract in CI against the expected response shapes","Log the raw 2xx body whenever response validation fails to speed up contract-drift diagnosis","Keep client and backend response types defined in one shared schema","Watch for gateway/proxy rewrites that can alter 2xx JSON bodies"],"tags":["api","validation","response-shape"],"backgroundTag":"unexpected-response-shape","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}