Skip to content

fix(act-http): tRPC actor deny maps to UNAUTHORIZED (401), not 500 (ACT-1286) - #1291

Merged
Rotorsoft merged 1 commit into
masterfrom
act-1286-trpc-actor-401
Jul 19, 2026
Merged

fix(act-http): tRPC actor deny maps to UNAUTHORIZED (401), not 500 (ACT-1286)#1291
Rotorsoft merged 1 commit into
masterfrom
act-1286-trpc-actor-401

Conversation

@Rotorsoft

Copy link
Copy Markdown
Owner

Summary

Fixes #1286. The trpc() generator resolved options.actor() inside the mutation's main try, so a "throw to deny" from the extractor (a plain Error) fell through to_trpc_errortoApiError(500) → INTERNAL_SERVER_ERROR. Hono's authenticated middleware returns 401 UNAUTHORIZED for the same deny. The guide explicitly promises the throw-to-deny semantics are identical (auto-generated-api.md:189,249,348) — unlike #1280 this divergence is promised NOT to exist.

Fix

Resolve the actor in its own try that maps any extractor throw to TRPCError({ code: "UNAUTHORIZED" }) (preserving the message), distinct from a framework error inside app.do (which the main try still maps via to_trpc_error).

Proof (red-first)

New tests over the generated router:

× actor 'throw to deny' surfaces as UNAUTHORIZED (401), matching Hono (#1286)
  expected 'INTERNAL_SERVER_ERROR' to be 'UNAUTHORIZED'

Plus a non-Error deny → 401 (branch coverage). The doc already described this behavior; the code now matches it, so no doc change.

Scope

Internal to the generator; the exported trpc(...) signature is unchanged. 100% coverage.

Gates

Typecheck ✅ · tests ✅ · 100% coverage ✅ · lint ✅ · build ✅.

rfc-gate: exempt — no new public surface. The snapshot delta is the internal mutation-handler source text in trpc/index.ts; no export added or changed.

Closes #1286

🤖 Generated with Claude Code

…00 (#1286)

The trpc() generator resolved the actor inside the mutation's main try, so a
"throw to deny" from the extractor (a plain Error) fell through to_trpc_error
→ toApiError(500) → INTERNAL_SERVER_ERROR, while Hono's `authenticated`
middleware returns 401. The auto-generated-API guide explicitly promises the
throw-to-deny semantics are identical (both UNAUTHORIZED).

Resolve the actor in its own try that maps any extractor throw to
TRPCError({code: "UNAUTHORIZED"}) (preserving the message), distinct from a
framework error inside app.do. Adds a red-first test (Error + non-Error deny
→ 401) matching Hono.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Rotorsoft Rotorsoft added bug Something isn't working priority:medium Nice-to-have improvements area:api API/Backend labels Jul 19, 2026
@Rotorsoft Rotorsoft self-assigned this Jul 19, 2026
@Rotorsoft
Rotorsoft merged commit ff77303 into master Jul 19, 2026
15 checks passed
@Rotorsoft
Rotorsoft deleted the act-1286-trpc-actor-401 branch July 19, 2026 13:41
@github-project-automation github-project-automation Bot moved this from Todo to Done in Act Roadmap 2026 Jul 19, 2026
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version @rotorsoft/act-http-v1.8.8 🎉

The release is available on:

Your semantic-release bot 📦🚀

@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version @rotorsoft/act-tck-v1.27.13 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:api API/Backend bug Something isn't working priority:medium Nice-to-have improvements released

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

tRPC actor-deny surfaces as 500, not 401 — diverges from Hono and contradicts the guide's 'identical throw-to-deny' promise

1 participant