{"record":{"id":"3d6926241e5272d5","repo":"gitbutlerapp/gitbutler","slug":"invalid-message-format","errorCode":null,"errorMessage":"Invalid message format","messagePattern":"Invalid message format","errorType":"validation","errorClass":"Error","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/2497b8007aa4a1922dae9a805b32ffe5b5037785/packages/shared/src/lib/login/loginService.ts#L99-L135","documentation":"Thrown inside the success handler of LoginService.finalizeAccount (packages/shared/src/lib/login/loginService.ts:117). After POST sessions/finalize returns response.ok, the parsed JSON body must contain a string message field; if isStr(data.message) fails, this Error is thrown. sendPostRequest catches it and converts it to a LoginResponse of type \"error\" with errorCode \"network_error\" and errorMessage \"Invalid message format\", so callers receive an error-shaped result object rather than a thrown exception.","triggerScenarios":"POST {api}/sessions/finalize with {email, login} returns a 2xx status whose body is missing message, has message of the wrong type (number, object, null), or is a JSON-parsed HTML page (proxy interstitial, captive portal) with no message field.","commonSituations":"publicApiBaseUrl pointing at the wrong host (a website or gateway answering 200 with HTML), backend version drift changing the finalize payload contract, middleware rewriting the body, or a staging environment with a different API shape.","solutions":["Log or curl the sessions/finalize response body to see the actual payload shape being validated","Verify the HttpClient publicApiBaseUrl points at the real GitButler API origin (URL resolves under /api/)","Check the backend/API version contract: sessions/finalize must return {message: string} on success","If the endpoint legitimately omits message, relax the handler to tolerate its absence instead of throwing"],"exampleFix":"// before\n(data) => {\n\tif (!isStr(data.message)) throw new Error(\"Invalid message format\");\n\treturn { message: data.message };\n}\n\n// after — tolerate an absent message from a 2xx finalize response\n(data) => ({ message: isStr(data.message) ? data.message : \"Account finalized\" })","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"type LoginSuccess<T> = { type: \"success\"; data: T };\n\nfunction isLoginSuccess<T>(res: LoginResponse<T>): res is LoginSuccess<T> {\n\treturn res.type === \"success\";\n}","tryCatchPattern":"// finalizeAccount does not reject — it resolves with an error response.\nconst res = await loginService.finalizeAccount(email, username);\nif (res.type === \"error\") {\n\t// \"Invalid message format\" arrives as res.errorMessage with errorCode \"network_error\"\n\tshowError(res.errorMessage);\n} else {\n\tshow(res.data.message); // narrowed: data.message is a string\n}","preventionTips":["Contract-test that sessions/finalize returns {message: string} on 2xx in CI","Point HttpClient at the correct API origin before login flows run","Log raw login response payloads in dev builds to catch schema drift early"],"tags":["login","account","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"}