gitbutlerapp/gitbutler · error
Invalid token format
Error message
Invalid token format
What it means
LoginService.confirmPasswordReset requires the 2xx response of POST sessions/confirm_new_password to carry a string `token` (the post-reset session token). When `data.token` is missing or not a string, the successHandler throws 'Invalid token format'. sendPostRequest converts the throw into an error LoginResponse (errorCode network_error), so the password reset flow reports failure even though the reset may have succeeded server-side.
Solutions
- Inspect the raw 2xx response body for sessions/confirm_new_password to see whether/where `token` is returned
- Check whether the backend still returns a session token on password reset; if it removed that behavior, update the client flow to not expect a token (e.g. require an explicit login afterwards)
- Align client/server versions or environments so the response contract matches
- If a token can legitimately be absent, treat it as optional and fall back to a login step instead of throwing
Example fix
// before
return { message: data.message, token: data.token };
// after
return {
message: data.message,
token: isStr(data.token) ? data.token : null,
}; // caller then falls back to loginWithEmail when token is null Defensive patterns
Strategy: try-catch
Validate before calling
const res = await fetch(base + "sessions/confirm_new_password", { method: "POST", ... });
const data = await res.json();
if (res.ok && typeof data?.token !== "string") {
console.warn("no session token in reset confirmation response", data);
} Type guard
function hasResetToken(data: unknown): data is { token: string } {
return typeof data === "object" && data !== null && typeof (data as any).token === "string";
} Try / catch
const result = await loginService.confirmPasswordReset(token, newPassword, confirm);
if (result.type === "error" && result.errorMessage === "Invalid token format") {
// Reset likely succeeded but no session token was returned; fall back to explicit login
await loginService.loginWithEmail(email, newPassword);
return;
} Prevention
- Confirm with the backend team whether confirm_new_password returns a session token, and encode that in the shared response type
- Fall back to an explicit login when no token is present instead of failing the whole flow
- Add contract tests covering the reset-confirmation response
- Treat the reset token as single-use: on this failure do not retry the same request
When it happens
Trigger: POST sessions/confirm_new_password returns HTTP 200 with a JSON body that lacks a string `token` field (or has a non-string value), tripping isStr(data.token) at line 137.
Common situations: Backend changed to no longer auto-login after reset (no token returned); server returns the token under a different key name; version mismatch between app and API; proxies or mocks that strip or reshape the success payload.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Invalid message 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/1b7ab58bea556692.
Report an issue: GitHub.
Appendix: source
Thrown at packages/shared/src/lib/login/loginService.ts:137
},
);
}
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) => {
if (!isStr(data.message)) throw new Error("Invalid message format");
if (!isStr(data.token)) throw new Error("Invalid token format");
return { message: data.message, token: data.token };
},
);
}
async resetPassword(email: string): Promise<LoginResponse<{ message: string }>> {
return await this.sendPostRequest("sessions/forgot_password", { email }, (data) => {
if (!isStr(data.message)) throw new Error("Invalid message format");
return { message: data.message };
});
}
async loginWithEmail(email: string, password: string): Promise<LoginResponse<string>> {
return await this.sendPostRequest("sessions/login_with_email", { email, password }, (data) => {
if (!isStr(data.token)) throw new Error("Invalid token format");
return data.token;
});
}View on GitHub (pinned to 58e5313667)