Skip to content

/auth/* 转发层把内部请求对象直接交给 better-auth:未知 auth 子路径(如 POST /api/v1/auth/login)返回 500 并外漏内部 TypeError,而非干净 404/405 #5085

Description

@os-zhuang

在 #5078 的实测定级 boot 中发现(2026-08-04,origin/main @ 29c6c9d,showcase,真实 boot 非 grep 推断),未认领。查重:headers.get 全仓开放 issue 零命中。

实测

POST /api/v1/auth/login
→ HTTP 500
{"success":false,"error":{"code":"INTERNAL_ERROR","message":"request.headers.get is not a function","httpStatus":500}}

阳性对照:POST /api/v1/auth/sign-in/email(better-auth 真实路由)→ 200 + set-cookie: better-auth.session_token=…,同一 boot 同一转发层,工作正常。

判读

request.headers.get is not a function 表明 /auth/* 的某条转发路径把内部请求形状(headers 为普通对象)直接交给了 better-auth 的 fetch 风格 handler(其调用 request.headers.get())。已知路由(sign-in/email)工作正常,说明主转发路径有正确的 Request 转换 —— 疑为某个次级/回退分支漏转换。两个问题叠加:

  1. 错误类别错了:不存在的 auth 子路径该是 404/405,现在是 500 —— /auth/login 是极易被猜到的路径(行业惯用名),真实用户/集成方会撞上;
  2. 内部错误文案外漏:raw TypeError message 进了响应体 —— 仓内 5xx 本有 looksLikeInternalErrorLeak 消毒惯例(error-envelope 一族),此路径绕过了它。

落点初判(分诊改标即可)

初判落 packages/runtime 的 auth 域转发层或其请求转换(domain:cli);若实读发现转换责任在 plugin-auth 侧,分诊时改 domain:identity。修法方向:未匹配的 auth 子路径在转发前给干净 404,或补全该分支的 Fetch Request 转换;任一情况下 5xx 出口过消毒。

复现

pnpm dev -- --fresh --seed-admin -p <port>(showcase)→ curl -X POST http://localhost:<port>/api/v1/auth/login。

关联:#5078(发现现场)、#3962(「失败说 HTTP」的错误统一先例)、ADR-0076(域属主)。

Activity

  1. baozhoutao commented on Aug 5, 2026

    @baozhoutao
    Contributor

    🔒 认领:PM 循环第 3 轮(cli 车道)
    会话:session_01VkPSGsX9o17MsGv3Lbxu2w
    分支:claude/issue-5085-auth-forward-unknown-subpath
    Worktree:objectstack-issue-5085
    域:domain:cli(issue 自判:若实读发现转换责任在 plugin-auth 侧,停下按 needs_decision 报告,由 PM 转 identity 车道,⛔ 不越面直改)
    文件面:packages/runtime 的 auth 域转发层(domains/auth.ts / dispatcher 请求转换段)+ 测试 + changeset。与同批 #4886(packages/rest)、#5225(examples)不相交。

    执行口径:两半都要 —— ①未匹配 auth 子路径干净 404/405(不把内部请求形状交给 better-auth 的 fetch handler);②任何 5xx 出口过 looksLikeInternalErrorLeak 消毒惯例,raw TypeError 不外漏。同日 churn(高):domains/auth.ts 与 dispatcher 今日刚被 #5385 重签名(context 首参、context.kernel),issue 基线 29c6c9d 已旧,以 origin/main 为准复现(阳性对照 sign-in/email 应保持 200)。


    Generated by Claude Code

  2. baozhoutao commented on Aug 5, 2026

    @baozhoutao
    Contributor

    认领解除(cli 车道 PM,session_01VkPSGsX9o17MsGv3Lbxu2w,2026-08-05 06:5xZ):本单第 3 轮派发的 dev 已被维护者手动中止,零推送、零报告、无远端分支 —— 无需回滚任何东西,前期认领评论中的执行口径与 churn 注记对下一次派发仍然有效。摘 assignee 与 pm:dispatched,回 pm:queue。是否重派等维护者示意(被维护者中止的任务不由 PM 自行重启)。


    Generated by Claude Code

  3. self-assigned this
    on Aug 6, 2026
  4. baozhoutao commented on Aug 6, 2026

    @baozhoutao
    Contributor

    门解除 + 重新认领(PM 会话 session_016FNvXhtSdnEGEfLEsMmvxh,cli 车道)

    前任 PM(session_01VkPSGsX9o17MsGv3Lbxu2w)06:00Z 记的「维护者手动中止」经维护者本人确认为误判——那次是 dev 子代理零推送自行消失,前任推断成手动中止并立了「未经示意不得重派」的门。维护者已明确示意未中止,门解除,本单正常重派。前任认领评论里的执行口径与 churn 注记仍有效,一并沿用。

    • 分支:claude/issue-5085-auth-forward-unknown-subpath
    • 工作树:../objectstack-5085(os-dev 自建)
    • 文件面:packages/runtime 的 auth 域转发层(domains/auth.ts / dispatcher 请求转换段)+ 测试 + changeset。
    • 执行口径两半:①未匹配 auth 子路径干净 404/405(不把内部请求形状交给 better-auth 的 fetch handler);②任何 5xx 出口过 looksLikeInternalErrorLeak 消毒,raw TypeError 不外漏。
    • churn 注记:domains/auth.ts 与 dispatcher 已被 fix(runtime): 每个请求从自己解析出的 kernel 取服务 —— 多租户 host 上两个请求不再互相串改 (#5155) #5385 重签名(context 首参、context.kernel),issue 基线 29c6c9d 旧,以 origin/main 为准复现(阳性对照 sign-in/email 应保持 200)。

    Generated by Claude Code

  5. baozhoutao commented on Aug 6, 2026

    @baozhoutao
    Contributor

    开工(cli 车道 dev,受 PM 会话 session_016FNvXhtSdnEGEfLEsMmvxh 02:42Z 重派 [门解除已确认误判])。

    分支:claude/issue-5085-auth-forward-unknown-subpath
    工作树:../objectstack-5085(基线 origin/main ef4efa8,非 issue 里的 29c6c9d)

    已在最新 main 静态复现并定位到确切成因(与 issue「疑为某次级/回退分支漏转换」判读一致,落点确实在 packages/runtime,不涉 plugin-auth):

    • 正常端点 POST /auth/sign-in/email → 200:走 AuthPlugin 的 /auth/* 通配(plugin-hono-server),转发 c.req.raw(真实 Fetch Request),better-auth 正常路由。
    • 未知子路径 POST /auth/login → 500 泄漏:命中 packages/runtime/src/dispatcher-plugin.ts:741 的遗留显式路由 server.post('${prefix}/auth/login'),它把 IHttpServer 的内部 req(headers 是普通对象,非 Headers)经 handleAuth('login', …, { request: req }) 直接交给 better-auth 的 fetch handler,后者调 request.headers.get() → TypeError: request.headers.get is not a function → 抛到 errorResponseBase(:469),而 looksLikeInternalErrorLeak 只匹配 SQL/driver dump、不匹配 TypeError,故 raw message 原样出体(与 issue 贴的响应体逐字吻合)。/login 本就不是 better-auth 端点。

    修法(两半,均在 packages/runtime,责任面属 runtime 转发层):

    1. 半一 干净 404:删除 dispatcher-plugin.ts 的遗留 /auth/login 显式路由 —— 它是唯一把非 Fetch 请求喂给 better-auth 的产出方(contract-first:修产出方,不在消费方加宽容)。删后 /auth/login 与所有其它未知子路径一样落到 /auth/* 通配 → better-auth 用真实 Request 给出它自己的干净 404,「判端点归属」的责任正确留在 better-auth 侧(无需 runtime 内建 better-auth 路由表,故不触 plugin-auth 边界)。
    2. 半二 5xx 出口消毒:domains/auth.ts handleAuthRequest 给 handleRequest 调用加 try/catch —— better-auth 抛出属非预期内部故障,按 sendError 的显式状态直通覆盖 400–599,5xx 的原始驱动报错绕过全部泄漏启发式直达客户端(metadata-protocol 有活体产出方) #5437/fix(rest): a declared 5xx no longer ships its own message to the client (#5437) #5464 现行惯例无条件扣留原文(→ INTERNAL_ERROR_MESSAGE,保状态与 code,原始错误落服务端日志),覆盖 cloud AuthProxyPlugin 等任何调用方,防任何 raw 内部错外漏。

    阳性对照:sign-in/email 走的是通配路径,两处改动都不碰它,保持 200。接下来写回归用例(throw→消毒 500、遗留路由已摘)+ pnpm --filter @objectstack/runtime test/typecheck + changeset,PR 开出等 CI 收敛再交报告。


    Generated by Claude Code

  6. baozhoutao commented on Aug 6, 2026

    @baozhoutao
    Contributor

    继任接手 + 重派(cli 车道 PM,会话 session_01DWUR56YsttL5sTF72Q75TQ;前任 session_016FNvXhtSdnEGEfLEsMmvxh 03:07Z 注销,本单为交接的唯一在飞项)

    现状核验:02:53Z 开工的 dev 随前任会话注销而死 —— 远程无 claude/issue-5085-auth-forward-unknown-subpath 分支、无 PR、零推送。其开工评论的定位(dispatcher-plugin.ts:741 遗留 /auth/login 显式路由把内部 req 交给 better-auth fetch handler;looksLikeInternalErrorLeak 不匹配 TypeError)与两半修法作为交接诊断沿用,但零推送即零验证,重派 dev 须在最新 origin/main 上先复核再实现。

    • 认领:继续既有认领(assignee 与 pm:dispatched 不变),本评论记录会话交接
    • 分支:claude/issue-5085-auth-forward-unknown-subpath(不变)
    • Worktree:objectstack-issue-5085(本会话容器全新,dev 自建)
    • 域:domain:cli;文件面:packages/runtime(dispatcher-plugin.ts / domains/auth.ts)+ 测试 + changeset;实读若发现责任面在 plugin-auth 则停手报 needs_decision 转 identity,⛔ 不越面直改
    • 执行口径两半不变:①未知 auth 子路径干净 404/405;②任何 5xx 出口过消毒,raw TypeError 不外漏;阳性对照 sign-in/email 保持 200
    • churn:以最新 origin/main(5e3c83b)为准;fix(runtime): 每个请求从自己解析出的 kernel 取服务 —— 多租户 host 上两个请求不再互相串改 (#5155) #5385 已重签 domains/auth.ts(context 首参、context.kernel),issue 基线 29c6c9d 与死 dev 基线 ef4efa8 均旧

    Generated by Claude Code

  7. removed their assignment
    on Aug 6, 2026
  8. baozhoutao commented on Aug 6, 2026

    @baozhoutao
    Contributor

    认领解除(cli 车道 PM,session_01DWUR56YsttL5sTF72Q75TQ,2026-08-06 04:1xZ):03:15Z 重派的 dev 被维护者手动中止(宿主明确回报 stopped by the user、不会恢复——本次是宿主信号,非推断,与 08-05 06:00Z 那次误判不同质)。零推送、无远端分支,无需回滚。摘 assignee 与 pm:dispatched,回 pm:queue。是否重派等维护者示意(被维护者中止的任务不由 PM 自行重启)。既有评论中的执行口径、churn 注记与 02:53Z 死 dev 的定位诊断对下次派发仍然有效。


    Generated by Claude Code

  9. self-assigned this
    on Aug 6, 2026
  10. baozhoutao commented on Aug 6, 2026

    @baozhoutao
    Contributor

    恢复认领 + 重派(cli 车道 PM,session_01DWUR56YsttL5sTF72Q75TQ,2026-08-06 04:2xZ):维护者已示意「重派,继续循环」,04:1xZ 中止门解除。分支 claude/issue-5085-auth-forward-unknown-subpath、worktree、执行口径与 churn 注记全部沿用 03:15Z 认领评论;基线取最新 origin/main(现 889ae47)。


    Generated by Claude Code

  11. added a commit that references this issue on Aug 6, 2026
  12. baozhoutao commented on Aug 6, 2026

    @baozhoutao
    Contributor

    验收:ACCEPT(cli 车道 PM,session_01DWUR56YsttL5sTF72Q75TQ,2026-08-06 05:0xZ)→ PR #5774

    转 ready + 挂 auto-merge 入队;落地后按两读数核验(queue refs + origin/main log),MERGED 后接续派 #5613(runtime 串行队列)。


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions