Skip to content

fix(plugin-auth): 短信日配额拒发时 OTP 端点回 500 而非 429 —— deliverPhoneOtp 抛的是普通 Error #6039

Description

@hotlong

背景

#2814 把短信总量成本闸落在 SmsService.send()(service 层统一扣减点),超限时返回 SendSmsResult { status: 'failed', error: 'TOO_MANY_REQUESTS: daily SMS quota exhausted' }。诉求第 3 点要求 OTP / 邀请路径回 429 TOO_MANY_REQUESTS,与按号码冷却一致。

实测结论:这个语义到不了调用方,而且原因完全在 auth 端点侧,service 层无论怎么写都够不着。

实测证据

  1. packages/plugins/plugin-auth/src/auth-manager.tsdeliverPhoneOtp()

    if (result.status === 'failed') {
      throw new Error(`Phone OTP could not be sent: ${result.error ?? 'SMS delivery failed'}`);
    }
    

    抛的是普通 Error,不是 APIErrorsendPhoneInviteSms() 同理(throw new Error('Invitation SMS failed: …'))。

  2. better-call@1.3.7(better-auth 的路由层)dist/router.mjs 第 93-98 行:

    if (isAPIError(error)) return toResponse(error);
    console.error(`# SERVER_ERROR: `, error);
    return new Response(null, { status: 500, statusText: "Internal Server Error" });
    

    isAPIError 的判定是 error instanceof APIError || error?.name === "APIError"dist/utils.mjs:57)。

  3. 直接量:普通 Errorinstanceof APIErrorfalsenew APIError('TOO_MANY_REQUESTS').statusCode429

即:配额拒发 → 调用方拿到 HTTP 500,且响应体为 nullTOO_MANY_REQUESTS 只出现在服务端日志里)。同一端点上按号码闸走的是 hooks.before 里的 assertPhoneOtpSendAllowed(),它抛的 APIError('TOO_MANY_REQUESTS'),正常回 429 —— 于是同一个端点上两道墙的对外表现不一致,正好是 #2814「两道墙从外面看应当一样」的反面。

建议修法(identity 车道)

deliverPhoneOtp / sendPhoneInviteSms 里识别 service 层信封上的 TOO_MANY_REQUESTS: 前缀,改抛 APIError('TOO_MANY_REQUESTS', …);其余失败仍按现状(传输故障回 500 是合理的)。文案沿用按号码闸的措辞,不泄露配额剩余细节。

@objectstack/service-sms 已导出常量 SMS_QUOTA_EXCEEDED_CODE / SMS_QUOTA_EXCEEDED_ERROR 供比对,但 plugin-auth 依赖 service-sms 会造成反向依赖 —— 更可能的做法是像 otp-send-guard.tsnormalizePhoneNumber 那样在本地写死这一个码并注明出处。

范围说明

本 issue 从 #2814 的实现中分出:#2814 的文件面被限定在 packages/services/**,⛔ 不动 plugin-auth,所以此处如实记录而非顺手改。#2814 的 PR 会写明「OTP 路径当前得到 500」这一事实。

Blocked-by: #2814
(#2814 的实现 PR #6042 仍 open —— service 层的 TOO_MANY_REQUESTS: 失败信封尚未落 main,本单要识别的正是那个前缀。分诊 2026-08-06 于 origin/main 9e3709a 核过:SMS_QUOTA_EXCEEDED_CODE / daily SMS quota 零命中,阳性对照 packages/services/service-sms 目录存在。)


Generated by Claude Code

Activity

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