gitbutlerapp/gitbutler · error · Error

Invalid message format

Error message

Invalid message format

What it means

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.

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 2497b8007a)

Solutions

  1. Log or curl the sessions/finalize response body to see the actual payload shape being validated
  2. Verify the HttpClient publicApiBaseUrl points at the real GitButler API origin (URL resolves under /api/)
  3. Check the backend/API version contract: sessions/finalize must return {message: string} on success
  4. If the endpoint legitimately omits message, relax the handler to tolerate its absence instead of throwing

Example fix

// before
(data) => {
	if (!isStr(data.message)) throw new Error("Invalid message format");
	return { message: data.message };
}

// after — tolerate an absent message from a 2xx finalize response
(data) => ({ message: isStr(data.message) ? data.message : "Account finalized" })
Defensive patterns

Strategy: type-guard

Type guard

type LoginSuccess<T> = { type: "success"; data: T };

function isLoginSuccess<T>(res: LoginResponse<T>): res is LoginSuccess<T> {
	return res.type === "success";
}

Try / catch

// finalizeAccount does not reject — it resolves with an error response.
const res = await loginService.finalizeAccount(email, username);
if (res.type === "error") {
	// "Invalid message format" arrives as res.errorMessage with errorCode "network_error"
	showError(res.errorMessage);
} else {
	show(res.data.message); // narrowed: data.message is a string
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@2497b8007a (2026-08-17). Data as JSON: /api/errors/3d6926241e5272d5. Report an issue: GitHub.