fix(api): read the error code before the status

A read-failure primitive decided from the status alone, so a 401 from a
step-up or re-proof gate would have read as an ended session and sent
the reader to sign-in. The code now decides first and the status is the
fallback for codes it does not know.

The two invitation routes answer an unknown name or e-mail with
sharing.invite.no_account. Both have a second 404 with a different next
step, and since every error now carries a code the clients match on it
instead of on the status. The status defaults move into a module the
client can import, and the contract text no longer says these arms
carry no code.
This commit is contained in:
Marc-André Bombeck committed 2026-10-20 09:48:15 +02:00
1 parent 0057b03699
commit c300574a8e
20 files changed
+212 -128

No files matched your search

@@ -58,6 +58,21 @@ describe("readFailureKind", () => {
expect(readFailureKind(new TypeError("Failed to fetch"))).toBe("failed");
expect(readFailureKind(null)).toBe("failed");
});
it("reads the code before the status", () => {
const withCode = (status: number, errorCode: string) =>
readFailureKind(new ApiError("x", status, { errorCode }));
// A step-up or re-proof gate answers 401 on a session that is still good:
// sending that reader to sign-in would end it.
expect(withCode(401, "auth.stepup.required")).toBe("failed");
expect(withCode(401, "auth.reproof.required")).toBe("failed");
expect(withCode(401, "auth.mfa.code_invalid")).toBe("failed");
expect(withCode(401, "auth.token.expired")).toBe("unauthenticated");
expect(withCode(401, "http.unauthenticated")).toBe("unauthenticated");
// An unknown code falls back to the status.
expect(withCode(401, "auth.something.new")).toBe("unauthenticated");
expect(withCode(403, "http.forbidden")).toBe("refused");
});
});
describe.each([