RankHub

API 目前处于 Early Preview 阶段, 可能存在诸多不完善与错误之处,欢迎加群交流反馈:加入官方交流群(1073434398)

错误与排障

协议错误码原文、同意页/内嵌的拒绝理由、业务接口的状态码语义,以及按症状反查的排查表。

协议错误体

/v1/oauth/** 与 /v1/oidc/** 的失败响应恒为两键,不套 v1 信封:

{ "error": "invalid_client", "error_description": "Unknown or inactive OAuth client" }
error常见 HTTP典型 error_description含义
invalid_client401Unknown or inactive OAuth client / Invalid client credentials客户端不存在、未过审、被停用,或 secret 错
invalid_request400Use only one client authentication methodBasic 与 body secret 同时传了
invalid_request400code is required / refresh_token is required / device_code is required少了必填参数
invalid_request400Invalid or already used authorization codecode 用过、伪造、或换了 client
invalid_request400Authorization code has expired超过 10 分钟
invalid_request400redirect_uri mismatch换令牌时的回调与授权时不一致
invalid_request400code_verifier is required (PKCE S256) / PKCE verification failedPKCE 缺失或不匹配
invalid_request400code is bound to a different DPoP key换了 DPoP 密钥
invalid_request400Unsupported grant_type: <值>grant 类型不支持
invalid_request400Unsupported scope: <值>请求了未注册/不存在的 scope
invalid_request400redirect_uri is not registered for this client回调地址没登记过
invalid_request400Only response_type=code is supported用了别的 response_type
invalid_request400PKCE S256 code_challenge is required公开客户端没带 challenge 或用了 plain
invalid_grant401Refresh token is invalid or expired / Refresh token does not belong to this client刷新令牌无效/换客户端用
invalid_grant401Refresh token replay detected; token family revoked重放:整族已撤销,必须重新授权
invalid_grant401Token family has been revoked族被撤(登出/撤销/泄露处理)
invalid_grant403Account is disabled账号被禁用
temporarily_unavailable429 / 503(限流或依赖不可用)按 Retry-After 退避后重试
server_error500Internal error服务端故障,附带 requestId 可用于上报
invalid_dpop_proof401proof 相关文案DPoP 校验失败;看 WWW-Authenticate 与 DPoP-Nonce
invalid_token200The access token is invalid or expireduserinfo 专用:失败也回 200

不要在客户端按 error_description 分支

error_description 是给人看的,文案可能微调;请按 error 分支,error_description 只用于日志与提示。

同意页 / 内嵌的拒绝理由

内嵌模式(/oauth/embed)在渲染前就做完预校验,失败会以 deny= 回给页面,页面再把理由 postMessage 给宿主:

理由含义
invalid_request缺 client_id 或 response_type 不对
invalid_client客户端不存在 / 未过审 / 被停用
redirect_uri_not_registeredredirect_uri 没登记或为空
scope_not_allowed请求的 scope 超出该客户端允许集
pkce_required公开客户端没带 code_challenge
state_required内嵌模式 state 必填
prompt_none_unsupported内嵌模式不支持 prompt=none
origin_not_registered宿主 origin 没登记进 embed_origins
service_unavailable依赖未就绪(稍后重试)

JS SDK 侧的错误码(not_configured / popup_blocked / timeout …)见前端接入。

业务接口状态码

状态含义处理
200 / 201成功(读 / 建号)—
202已接受(如删除申请)看 code:common.accepted = 删除申请受理
401明确未认证刷新令牌;刷新失败重新授权
403已认证但无权:scope 不足、要求浏览器会话、该形状只给第一方、写入被暂停、删除要用户同意看业务码:只有"缺 scope"能靠重新授权解决,其余三种不能靠重试
404路径不存在或资源不存在检查路径拼写与版本前缀(/v0、/v1)
409冲突(幂等键参数不一致、确认令牌过期/已用/payload 变了等)换参数或重新发起,不要原样重试
413请求体过大(上限 1 MiB)分批提交
426客户端版本过低(仅原生 App 登录态)升级 App
428需要用户先做一次确认:写入确认(游戏面)或敏感操作二次验证(浏览器会话)游戏面按 data.confirmation 重放;会话面走 step-up 流程
429限流按 Retry-After 退避
503鉴权或数据服务暂不可用可重试,不要清会话

/v1/** 的错误体是信封:

{
  "code": "auth.unauthenticated",
  "message": "Authentication is required",
  "data": null,
  "requestId": "req_…"
}

失败响应里 data 通常为 null

唯一例外是 428 game.writeConfirmationRequired:它必须带机读的 data.confirmation (确认令牌),否则客户端没法完成确认重放。其余失败请只按 code 分支。

游戏面的四个专属业务码

/v1/games/** 的权限收紧带来四个新码。它们都会出现在公开的 GET /v1/catalog/codes 里,所以文案里不含任何阈值或判定细节 —— 也别试图从文案反推规则。

codeHTTP中文文案English什么时候出现怎么处理
game.firstPartyOnly403该操作只能由 RankHub 客户端发起This operation is only available to the RankHub client第三方令牌调了仅第一方的形状换用对第三方开放的 /v1/games/** 形状;不要重试
game.deletionConsentRequired403删除档案需要用户在 RankHub 中同意Deleting a profile requires the user's approval in RankHub第三方直接 DELETE 档案改用 POST .../deletion-requests,等用户确认
game.writeSuspended403该应用的写权限已被暂停Write access for this application is suspended该应用的全部写接口(读与登录不受影响)停下排查数据来源,联系平台恢复
game.writeConfirmationRequired428该写入需要用户确认后重试This write requires user confirmation写入被判定为"低频、单次可疑"把 data.confirmation.approveUrl 交给用户,确认后用 confirmationToken 重放同一份 payload

game.firstPartyOnly 只出现在仅第一方开放的形状上(以及第三方打 /v0 面时, 曲目别名与谱面评论两族除外)。完整清单见权限范围与可用 API。

428 的 data 形状:

{
  "code": "game.writeConfirmationRequired",
  "message": "该写入需要用户确认后重试",
  "data": {
    "confirmation": {
      "confirmationToken": "wc_7Q2…",
      "approveUrl": "rankhub://write-confirmation?token=wc_…",
      "expiresAt": "2026-10-22T19:10:00Z"
    }
  },
  "requestId": "req_…"
}

重放细节(一次性、30 分钟、payload 变了就 409 common.conflict)见 成绩读取与写入。

删除申请的成功码:common.accepted

删除申请成功返回 202 + code: "common.accepted"("已接受")。 别把它当成失败:它表示"申请已受理,等用户处理",data 里有 requestId 与 expiresAt。

别把 202 当 200

异步提交成功是 202;删除申请受理的 code 是 common.accepted。 按 code 分支,不要只看 status === 202。

限流码

限流命中的错误信封是 429 + code: "common.rateLimited"(retryable: true), 并带 Retry-After(秒)。按它退避(带抖动);单批条数超限是 400 common.invalidArgument, 不是限流。详见业务 API 总览。

按症状查

症状最可能的原因怎么确认
授权页说应用不可用应用还在「审核中」/被停用开发者平台看状态
授权页 400 redirect_uri is not registered回调地址不完全一致(末尾斜杠、端口、大小写、http vs https)把两串贴一起逐字符比
授权页 400 PKCE S256 code_challenge is required公开客户端(none)没带 PKCE加 code_challenge + method=S256
换令牌 401 invalid_clientsecret 错 / 认证方式与注册不符 / Basic 与 body 混用只用一种方式;确认平台里选的是 basic 还是 post
换令牌 400 Invalid or already used authorization codecode 用第二次、超时、或换令牌时换了 client_id一次授权只换一次
换令牌成功但调接口 401用了 api.rankhub.cc 以外的地址,或令牌已过期只认 api.rankhub.cc;看 expires_in
调接口 403 requires <scope>scope 不够平台补 scope → 用户重新授权
调了只给第一方的形状(403 game.firstPartyOnly)该形状不对第三方开放不是路径写错,也不是缺 scope;改用 /v1/games/** 的开放形状
调 /v0/games/**、/v0/social/** 403 game.firstPartyOnly旧面本期只服务第一方(别名 / 评论例外)迁到 /v1/games/**;社交面暂时没有第三方替代
建号 POST /v1/games/{game}/profiles 返回 400缺该游戏的建号材料,或该游戏要从"已绑定账号"拉取而用户没绑对材料表逐个字段核对(sessionToken / playerFileV2 / phone+code / externalId+snapshot);notanote / musedash / osu 只需用户已绑定,见游戏档案
建号时传了 scores 但成绩没变该游戏 pushScores 为假,scores 被忽略(不是报错)改用 POST .../{profileId}/scores 逐条推
删档案 403 game.deletionConsentRequired第三方不能直删改用 POST .../deletion-requests
写成绩 403 game.writeSuspended该应用写权限被暂停应用详情看原因;联系平台,重试无效
写成绩 428 game.writeConfirmationRequired这次写入要用户确认用 data.confirmation.approveUrl 让用户确认,再带 confirmationToken 重放
带 confirmationToken 重放却 409 common.conflict令牌过期 / 已用 / payload 变了重放必须与上次完全一致;改了就重新发起
某个用户突然全部掉登录刷新令牌重放(并发刷新)导致整族撤销查你的刷新是否串行
id_token 验签失败JWKS 为空 / iss 不匹配 / 算法不在白名单直接 curl JWKS;核对 iss 是 https://auth.rankhub.cc
通用 OIDC 库初始化就失败发现文档不在标准路径显式配置发现地址
userinfo 返回 200 但没有用户它在 body 里回了 error: invalid_token检查 body 的 error 字段
内嵌 8 秒后 origin_not_registered来源没登记 / 被 frame-ancestors 拦联系我们登记 embed_origins
设备码流卡在 authorization_pending用户还没在确认页/App 里完成确认让用户打开 verification_uri 输入 user_code

上报问题要带什么

  • 出问题的接口(哪个 URL、哪个步骤);
  • 时间(精确到分钟,带时区);
  • 响应里的 X-Request-ID(或在信封里的 requestId)—— 服务端日志按它索引;
  • error / error_description 原文;
  • 不要贴 access_token / refresh_token / client_secret / 授权码 / DPoP proof。

服务端日志只记录 requestId、主体哈希、jkt 哈希,不会记录令牌、proof、nonce 或验证码。

本页内容

官方交流群二维码官方交流群问题反馈 · 版本资讯 · 成绩交流群号 1073434398