错误码与排查
每个错误码都说明了发生了什么、该怎么处理。先判断是让访客重新验证、修正配置,还是原样重试。
错误响应的格式
接口在失败时返回非 2xx 状态码和 JSON:{"error":"invalid-input-secret"}。字段缺失或格式不对时,还会带上出错的字段:{"error":"bad-request","field":"nonce"}。请求体必须是 JSON,Secret key 放在 Authorization: Bearer 请求头里,否则会得到 unsupported-media-type 或 secret-in-body。
联系我们排查问题时,请附上响应头里的 X-Request-Id。
按状态码处理
| 状态码 | 含义 | 该怎么做 |
|---|---|---|
| 400 · 409 · 413 · 415 · 422 | 请求格式不对,或许可、token 已过期、已用、对不上 | 代码问题先修正;验证类错误让访客重新验证(新的 intent + 重新渲染组件) |
| 401 · 403 | Secret key、Site key 或域名配置不对 | 修正配置,重试不会成功 |
| 402 | 额度用完且余额不足 | 充值后用同一个请求重试 |
| 408 · 429 · 503 | 超时、限流或暂时不可用 | 用相同参数和幂等键重试(429 按 Retry-After 等待);得到结果前不要执行业务 |
服务端接口错误码
来自 /intents、/challenge、/solve、/siteverify、/feedback 和 /grants。其中 /challenge 和 /solve 由组件调用,你通常只会在组件的错误回调里看到它们。
bad-requestHTTP 400 / 422所有接口请求体不是合法 JSON(400),或缺少必填字段、字段类型或格式不符(422,例如 nonce 太短)。响应里的 field 会指出是哪个字段。
怎么处理:按 field 修正请求。这是代码问题,原样重试不会成功。
unsupported-media-typeHTTP 415所有接口请求没有使用 Content-Type: application/json,例如沿用了 reCAPTCHA / hCaptcha 的表单编码请求。
怎么处理:改用 JSON 请求体,并设置 Content-Type: application/json。
secret-in-bodyHTTP 422所有接口请求体里带了 secret 字段,这是 reCAPTCHA / hCaptcha 的写法。
怎么处理:删掉请求体里的 secret,把 Secret key 放进 Authorization: Bearer 请求头。
body-too-largeHTTP 413所有接口请求体超过了大小上限(大多数接口为 4 KB),在读取请求时就被拒绝。
怎么处理:检查是否带了多余字段或大段数据;正常请求远小于这个上限。
payload-too-largeHTTP 413所有 JSON 接口JSON 请求体超过了大小上限。
怎么处理:同 body-too-large:去掉多余字段,正常请求远小于这个上限。
invalid-input-secretHTTP 401/intents、/siteverify、/feedback、/grants缺少 Secret key,或 Secret key 无效。
怎么处理:在 Authorization: Bearer 请求头里使用控制台的 Secret key(mc_sec_…)。更换密钥后旧密钥立即失效;不要误用 Site key。
origin-not-allowedHTTP 403/intents、/challenge、/solve页面所在域名或 intent 的 hostname 不在站点的允许域名中。
怎么处理:在站点设置里加入完整域名,子域名要单独添加;本地开发请加入 localhost。
invalid-sitekeyHTTP 403/challengeSite key 不存在,或站点已暂停、已删除。
怎么处理:核对组件的 data-sitekey,并在控制台确认站点处于启用状态。
nonce-conflictHTTP 409/intents这个 nonce 之前用不同的 action、hostname、session 或豁免创建过许可。
怎么处理:每次新的业务操作都生成新 nonce;只有重试同一次创建时才复用 nonce,并且字段必须完全相同。
intent-expired-or-consumedHTTP 409/intents用同一个 nonce 重试创建时,原许可已经过期(5 分钟)或已被兑换。
怎么处理:生成新的 nonce,创建新的许可。
invalid-or-expired-permitHTTP 400/challenge、/solve、/siteverify许可不存在、已过期或已用完,或者 token 与这次兑换的 intent_id、action、hostname、站点对不上。
怎么处理:让访客重新验证:服务端创建新的 intent,重新渲染组件。不要重试同一个请求。
invalid-input-responseHTTP 400/siteverifytoken 为空、格式错误、签名无效,或已超过有效期(约 2 分钟)。
怎么处理:让访客重新验证(新的 intent + 重新渲染组件)。
already-redeemedHTTP 409/siteverify这个 token 已经被兑换过。可能是重复提交,也可能是 10 分钟后又用同一幂等键重试。
怎么处理:按验证失败处理,让访客重新验证;检查是否有重复提交。
idempotency-conflictHTTP 409/siteverify同一个幂等键曾用于不同的 token、intent、action 或 hostname。
怎么处理:每次业务提交使用独立的幂等键;重试时所有参数必须和第一次完全相同。
insufficient-balanceHTTP 402/siteverify本月免费额度已用完,且充值余额不足。token 没有被消耗。
怎么处理:在控制台充值后,用同一个请求重试(token 仍需在有效期内)。
challenge-expiredHTTP 400/solve挑战在规定时间内没有完成。
怎么处理:组件会显示「重试验证」,通常无需处理;许可也过期时由服务端创建新的 intent。
invalid-solutionHTTP 400/solve工作量证明的结果不正确。
怎么处理:组件会自动处理;持续出现时请把请求编号发给我们。
binding-mismatchHTTP 403/solve挑战进行中,访客的网络发生了变化。
怎么处理:让访客在当前网络下重新验证。
invalid-feedbackHTTP 400/feedback回传的业务结果不合法,例如 decision_id 不属于这个站点、时间超出范围或字段格式不符。
怎么处理:核对 decision_id(来自 /siteverify 的响应)和各字段格式。
feedback-conflictHTTP 409/feedback同一个 event_id 已经提交过不同的内容。
怎么处理:新的事件使用新的 event_id;要修正之前的结果,用 supersedes_event_id 指向它。
invalid-or-expired-grantHTTP 400/grants、/intents挑战豁免无效、已过期、已撤销或次数已用完。
怎么处理:不带 trusted_grant_id,创建普通 intent。
grant-conflictHTTP 409/grants同一个 request_id 提交了不同的豁免内容。
怎么处理:重试时使用完全相同的请求;新的豁免使用新的 request_id。
rate-limitedHTTP 429所有接口请求过于频繁。
怎么处理:按 Retry-After 等待后重试同一个请求,不要循环重试。
request-timeoutHTTP 408所有接口请求体在规定时间内没有上传完。
怎么处理:检查网络后重试同一个请求。
unavailableHTTP 503所有接口验证服务或它依赖的组件暂时不可用。
怎么处理:用相同的参数和幂等键重试。在得到明确结果前,不要执行业务。
not-foundHTTP 404风险数据导出、验证过程记录、控制台资源不存在,或不属于当前账号。
怎么处理:核对站点 ID、intent ID 或订单号。
组件错误回调
组件通过 error-callback(HTML 用 data-error-callback)告诉你出了什么问题。除了下面这些,服务端的错误码(例如 origin-not-allowed、invalid-sitekey)也会原样传给回调。组件出错时会显示重试按钮,不会自动循环重试。
| 错误码 | 含义 | 怎么处理 |
|---|---|---|
intent-required | 渲染组件时没有提供 intent(data-intent 或 intent 选项)。 | 由服务端创建 intent,把 intent_id 填进组件。 |
network-error | 组件无法连接验证服务。 | 提示访客检查网络;组件会显示重试按钮。 |
request-timeout | 验证请求长时间没有完成,组件停止了等待。 | 访客点击重试即可;无需重新创建 intent。 |
invalid-response | 验证服务返回了无法识别的内容,通常是代理或 CSP 拦截了请求。 | 检查 connect-src 是否允许 https://captcha.moe,以及中间是否有代理改写响应。 |
challenge-expired | 挑战在完成前过期了。 | 组件会显示「重试验证」;许可也过期时,服务端需要创建新的 intent 并重新渲染组件。 |
challenge-budget-exceeded | 同一个许可尝试挑战的次数用完了。 | 服务端创建新的 intent,重新渲染组件。 |
unknown-error | 发生了未归类的错误。 | 让访客重试;持续出现请把浏览器控制台信息发给我们。 |
控制台与账号
这些错误只出现在控制台和账号相关的操作里,控制台会直接显示对应的中文提示。
展开 30 个错误码
demo-unavailableHTTP 503官网试用官网在线试用暂时不可用。
怎么处理:稍后再试;不影响你自己的站点。
unauthorizedHTTP 401控制台没有登录,或登录已过期。
怎么处理:重新登录。
invalid-credentialsHTTP 401 / 403登录、修改密码邮箱或密码不正确;修改密码时是当前密码不正确。
怎么处理:重新输入;忘记密码可以通过邮件重置。
email-unverifiedHTTP 403登录邮箱还没有验证。
怎么处理:查收验证邮件并点击其中的链接;没有收到可以重新发送。
email-takenHTTP 409注册这个邮箱已经注册过。
怎么处理:直接登录,或找回密码。
account-suspendedHTTP 403登录、邮箱验证账号已被 captcha.moe 停用:不能登录,名下站点也暂停验证。
怎么处理:发邮件到 supports@captcha.moe 了解原因或申请恢复。
admin-protectedHTTP 409管理后台不能在管理后台停用自己或其他管理员。
怎么处理:确需停用时,先由服务器运营方用 scripts/admin-role.sh 撤销对方的管理员身份。
invalid-emailHTTP 400注册邮箱格式不正确。
怎么处理:检查邮箱地址。
password-too-shortHTTP 400注册密码少于 12 位。
怎么处理:使用至少 12 位的密码。
password-lengthHTTP 400修改密码、重置密码新密码长度不符合要求(12 到 1024 位)。
怎么处理:换一个长度合适的密码。
captcha-requiredHTTP 400注册、登录、找回密码没有完成页面上的安全验证。
怎么处理:完成验证后再提交。
captcha-invalidHTTP 400注册、登录、找回密码安全验证已过期或已经用过。
怎么处理:重新完成验证后再提交。
invalid-or-expired-linkHTTP 400邮箱验证、重置密码邮件里的链接已过期(30 分钟)或已经用过。
怎么处理:重新发送邮件,使用最新的链接。
mail-unavailableHTTP 503找回密码、重发验证邮件邮件服务暂时不可用。
怎么处理:稍后再试。
confirmation-requiredHTTP 400删除账号删除账号时没有输入确认信息。
怎么处理:按提示输入确认内容。
balance-remainingHTTP 409删除账号账号仍有充值余额,暂时不能删除。
怎么处理:先申请退款或用完余额。
payment-pendingHTTP 409删除账号还有未完成的充值订单。
怎么处理:等订单完成或关闭后再删除。
refund-pendingHTTP 409删除账号还有正在处理的退款。
怎么处理:等退款完成后再删除。
invalid-nameHTTP 400站点设置站点名称不合法(过长或包含控制字符)。
怎么处理:换一个名称。
name-requiredHTTP 400站点设置站点名称为空。
怎么处理:填写站点名称。
invalid-originHTTP 400站点设置允许域名的格式不正确。
怎么处理:只填域名,例如 example.com,不含 https:// 和路径。
origins-requiredHTTP 400站点设置至少需要一个允许域名。
怎么处理:填写表单所在的域名。
too-many-originsHTTP 400站点设置允许域名的数量超过上限。
怎么处理:删除不用的域名,或拆分为多个站点。
invalid-modeHTTP 400站点设置验证模式不在可选范围内。
怎么处理:从 auto、interactive、passive 中选择。
invalid-dateHTTP 400用量查询日期格式不正确。
怎么处理:使用 YYYY-MM-DD。
invalid-rangeHTTP 400用量查询查询的时间范围不合法或太长。
怎么处理:缩短时间范围。
order-conflictHTTP 409充值同一个充值请求编号对应了不同的金额或内容。
怎么处理:刷新页面后重新发起充值。
pending-orders-limitHTTP 409充值未完成的充值订单太多。
怎么处理:先完成或等待已有订单过期。
no-databaseHTTP 501控制台服务端没有配置数据库(只会出现在开发环境)。
怎么处理:检查部署配置。
internal-errorHTTP 500控制台服务端内部错误。
怎么处理:稍后再试;持续出现请联系 supports@captcha.moe 并附上请求编号。
常见问题排查
组件没有出现
打开浏览器控制台:脚本加载失败通常是 CSP 没有允许 https://captcha.moe;看到 intent-required 说明页面没有拿到服务端创建的 intent_id。
组件报 origin-not-allowed
页面所在的域名不在站点的允许域名里。子域名要单独添加;本地用 localhost 打开时要加入 localhost,用 127.0.0.1 打开时要加入 127.0.0.1。
前端显示已验证,提交仍然失败
检查服务端是否读取了 moe-captcha-response 字段,兑换时的 intent_id、action、hostname 是否和创建时一致,以及 token 是否在 2 分钟内提交。组件显示「已验证」不代表可以放行。
控制台里的「校验成功」比「完成挑战」少很多
有些访客完成验证后没有提交表单,这是正常的。如果差得很多,通常是服务端还没有调用 /siteverify,或者兑换失败了——看一下服务端日志里的错误码。