Skip to content

Admin proxy discards upstream error detail behind admin_request_failed #115

Description

@Bccorb

Summary

The admin proxy handlers discard the upstream error whenever the auth API responds with a body that has no error key, replacing it with the constant admin_request_failed. Validation failures are the common case, because the auth API answers those with a Zod error body shaped { name, message }. The caller receives a 400 with no indication of which field was rejected or why.

Where

packages/core/src/handlers/admin.ts:57:

const data = await up.json();

if (!up.ok) {
  return {
    status: up.status,
    error: data?.error || "admin_request_failed",
  };
}

Every helper in that file routes through request, so this affects the whole admin surface: users, organizations, sessions, and auth events.

Impact

A concrete case from the admin dashboard: PATCH /admin/users/:id with phone: "" fails the auth API's UpdateUserSchema, which requires a phone number or null. The auth API returns a Zod error naming the phone field. The adapter drops that and returns {"error":"admin_request_failed"}.

From the dashboard's point of view, editing a user's roles failed with an opaque string, and nothing in the response pointed at the phone field. The auth API's own request logging records only the route, so the detail is not recoverable from logs either. Diagnosing it required reproducing the request by hand and bisecting the payload field by field.

The masking is worst exactly where the detail matters most, since a 4xx from validation is actionable by the caller while the current shape is not.

Suggested change

Preserve the upstream body when there is no error string, rather than collapsing it. For example, forward the parsed body under a stable key, or surface the upstream message when present, keeping admin_request_failed only as the last-resort fallback for an empty body. Any shape that carries the upstream detail through would have made the case above self-explanatory.

Worth checking whether other proxy paths in the adapter apply the same pattern, so the fix is consistent across them.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions