fix email account creation: body parser, error shape, and non-Error throws

adding an email account failed with a bare "failed to add account" toast. three
defects stacked, each hiding the next.

the email sidecar's http.ts reconstructs what the platform's middleware used to
provide, but only did two of three — bodyParser was never remounted, so every
write route read ctx.get('body') as undefined and POST /accounts threw on
body.provider before ever reaching the credentials.

its onError then read `.status` off the thrown custom-error, which carries
`statusCode`. every deliberate 4xx fell through to the 500 branch and had its
message replaced with "internal error", so a rejected IMAP login and a genuine
crash looked identical. it also answered JSON where the rest of the api answers
errors as plain text. now mirrors hono.ts's handler rather than inventing a
second shape.

useClient threw a plain object, so the ~33 sites narrowing with
`err instanceof Error ? err.message : <fallback>` always took the fallback and
discarded the server's message. now throws an ApiError subclass keeping both
status and message, so those sites start surfacing real errors.

only email reads ctx.get('body'); every other sidecar is a pure proxy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-07 19:31:31 +00:00
co-authored by Claude Opus 5
parent bffae5ef61
commit 7b7147001b
2 changed files with 40 additions and 9 deletions
+15 -1
View File
@@ -151,6 +151,20 @@ export const DELETE = async <T>(uri: string, payload?: any, baseUrl = '') => {
return parseBody<T>(res);
};
/** Thrown by every verb on a 4xx/5xx. A real `Error` subclass, because ~33 call sites narrow with
* `err instanceof Error ? err.message : <fallback>` — against the plain object this used to throw that test
* was always false, so every one of them showed its generic fallback and discarded what the server said.
* Keeps `status` and `message`, so code reading either is unaffected. */
export class ApiError extends Error {
constructor(
public status: number,
message: string,
) {
super(message);
this.name = 'ApiError';
}
}
const validateResponse = async (res: Response) => {
if (res.status >= 400) {
const { onError } = useClient.config;
@@ -158,7 +172,7 @@ const validateResponse = async (res: Response) => {
if (onError) {
onError({ status: res.status, message });
}
throw { status: res.status, message };
throw new ApiError(res.status, message);
}
};