gitbutlerapp/gitbutler · error
Invalid message format
Error message
Invalid message format
What it means
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.
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
Example fix
// before
(data) => {
if (!isStr(data.message)) throw new Error("Invalid message format");
return { message: data.message };
}
// after
(data) => ({ message: isStr(data.message) ? data.message : "Account finalized" }) Defensive patterns
Strategy: type-guard
Validate before calling
const res = await fetch(base + "sessions/finalize", { method: "POST", ... });
const data = await res.json();
if (res.ok && typeof data?.message !== "string") {
console.warn("finalize returned unexpected shape", data);
} Type guard
function hasMessage(data: unknown): data is { message: string } {
return typeof data === "object" && data !== null && typeof (data as any).message === "string";
} Try / catch
const result = await loginService.finalizeAccount(email, username);
if (result.type === "error") {
if (result.errorMessage === "Invalid message format") {
// 2xx body did not match the expected contract; log raw for diagnosis
console.error("finalize: unexpected response shape", result.raw);
}
showError(result.errorMessage);
return;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Invalid token format
- API returned version
- Aborting due to empty branch name
- Aborting due to empty
- Anthropic API error ( )
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/3d6926241e5272d5.
Report an issue: GitHub.
Appendix: source
Thrown at packages/shared/src/lib/login/loginService.ts:117
errorCode: "unknown_error",
errorMessage: "An unknown error occurred",
raw: error,
};
}
}
async finalizeAccount(
email: string,
username: string,
): Promise<LoginResponse<{ message: string }>> {
return await this.sendPostRequest(
"sessions/finalize",
{
email,
login: username,
},
(data) => {
if (!isStr(data.message)) throw new Error("Invalid message format");
return { message: data.message };
},
);
}
async confirmPasswordReset(
token: string,
newPassword: string,
passwordConfirmation: string,
): Promise<LoginResponse<{ message: string; token: string }>> {
return await this.sendPostRequest(
"sessions/confirm_new_password",
{
password_reset_token: token,
password: newPassword,
password_confirmation: passwordConfirmation,
},
(data) => {View on GitHub (pinned to 58e5313667)