主题
错误处理与重试
两套错误结构
标准 REST 错误
/api/v1/channel/*、/api/v2/channel/*,以及数据接口的鉴权、权限和交付异常使用 HTTP 状态码。 通常返回以下 Body;税务请求大小/整包并发限制的中间件错误可省略 details:
json
{
"code": "CHANNEL_ORDER_NOT_FOUND",
"message": "Channel order not found",
"details": {}
}code 是固定的字符串常量,程序里用它做分支判断;message 是给人看的,仅供排查。
PDF、同源 HTML 和发票分片签名下载的 403/404 可能只返回 {"detail":"..."},没有 code;仍按 HTTP 状态处理。该区别不适用于所有下载接口,具体以各接口参考为准。
校验失败时 details.errors 列出具体字段:
json
{
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": {
"errors": [
{ "type": "value_error", "loc": ["body"], "msg": "at least one collection type is required" }
]
}
}业务码信封
业务码信封使用 HTTP 200,结果在 success / code 里;该路径下的鉴权、权限、请求大小、限流和交付异常仍可返回非 2xx。先处理 HTTP 错误,再判断信封。 见获取数据。
业务码
code | 含义 | 处理 |
|---|---|---|
0 / 200 | 成功 | —— |
60003 | 采集、加工或交付准备中 | 15–30 秒后重试当前查询 |
60002 | 失败或无可交付数据 | 查询订单/产品的错误信息 |
10000 | 任务不存在 | 核对渠道编码与两类订单号 |
10001 | 参数、输出范围或发票汇总处理状态 | data.errorCode=INVOICE_RESULT_NOT_READY 表示汇总准备中,可等待重查;其他汇总处理错误按 errorCode、msg 联系平台处理来源或后台任务;参数和授权范围错误则修正请求 |
60002 有两种含义
「采集失败」和「查询无数据」都是 60002,msg 会给出具体原因 (如「查询无数据」「税务数据未匹配到已配置表单」「本任务未申请 PDF 报告」)。 需要区分时回到产品查询看 state 与 error_code。
HTTP 状态码
| 状态 | 典型 code | 处理 |
|---|---|---|
| 400 | INVOICE_CURSOR_INVALID、INVOICE_SCHEMA_RETIRED、ACK_BATCH_INVALID、LARGE_CHUNK_INVALID | 按对应交付规则修正参数或重新开始读取 |
| 413 | TAX_REQUEST_TOO_LARGE | 缩小请求体;税务 ALL 请求上限为 1 MiB |
| 416 | RANGE_INVALID、RANGE_REQUIRED | 使用单个显式起点的 Range,每次不超过 4 MiB |
| 429 | TAX_WHOLE_RESPONSE_LIMIT、TAX_READ_LIMIT | 遵循 Retry-After(如有),降低并发,整包可改用分页 |
| 503 | DELIVERY_NOT_READY、PART_INTEGRITY_FAILED | 等待当前交付准备或修复,再重试同一读取 |
| 401 | CHANNEL_AUTH_REQUIRED、INVALID_CHANNEL_KEY、CHANNEL_SIGNATURE_REQUIRED、INVALID_CHANNEL_SIGNATURE、CHANNEL_TIMESTAMP_EXPIRED | 核对认证头、时钟与 canonical,见鉴权与签名 |
| 403 | CHANNEL_ORDER_FORBIDDEN、REPORT_NOT_ENTITLED | 渠道与订单不匹配,或未获定制报告授权 |
| 404 | CHANNEL_ORDER_NOT_FOUND、PRODUCT_NOT_REQUESTED、RAW_ARTIFACT_NOT_AVAILABLE、HTML_REPORT_NOT_FOUND、HTML_REPORT_NOT_READY、INVOICE_ORDER_NOT_FOUND | 核对订单号;确认本单申请过该产品 |
| 409 | EXTERNAL_ORDER_CONFLICT、AGREEMENT_NOT_CONFIGURED、TAX_URL_MISSING、CHANNEL_REQUEST_REPLAYED、INVOICE_DELIVERY_MODE_MISMATCH、INVOICE_*_NOT_READY、INVOICE_*_EXPIRED | 读 code 再决定,不要盲目重试 |
| 403 | RAW_ARTIFACT_LINK_EXPIRED | 原始文件签名缺失、不完整、无效或过期;鉴权查询订单取得完整新链接 |
| 410 | RAW_ARTIFACT_EXPIRED、HTML_REPORT_EXPIRED、PUBLIC_RESULT_ACCESS_EXPIRED、ORDER_DATA_ACCESS_EXPIRED、ORDER_DATA_ACCESS_REVOKED、REPORT_DATA_EXPIRED、INVOICE_DATASET_EXPIRED | 按 code 区分文件或快照已清理、分享到期、资料到期及权限撤销;刷新链接不会延长资料期限 |
| 422 | INVALID_ACCESS_LIFETIME | 修正三类期限及其组合,分享天数不能超过资料保留天数 |
| 422 | VALIDATION_ERROR、INVALID_PRODUCT、INVALID_CALLBACK_URL、SUCCESS_URL_POINTS_TO_MANAGEMENT、EXIT_URL_POINTS_TO_MANAGEMENT、REPORT_TYPE_UNSUPPORTED | 修正请求 |
每个接口能返回哪些 code 是写在契约里的
接口参考每个接口的每个错误响应都列出了该接口实际可能返回的 code 及其含义, 本页是跨接口的汇总,以接口页为准;客户端应保留无法识别的 code 和 message,避免将新增错误码当成成功。
发票交付专属错误
/invoice/standard 或 /invoice/results 请求 STANDARD 时,异步汇总未就绪或失败会返回 HTTP 200、code=10001、success=false,具体代码位于 data.errorCode。只有 INVOICE_RESULT_NOT_READY 表示继续等待;INVOICE_RESULT_STORAGE_LIMIT、INVOICE_STANDARD_SOURCE_MISSING、INVOICE_RESULT_BUILD_FAILED 等应交平台处理存储、来源或任务,不能靠修改请求参数恢复。
普通 /invoice/all 使用页码分页,参数错误也可返回 HTTP 200、code=10001,data 为 null。Compatible_V1 的协议错误代码位于响应顶层 errorCode,不要与 STANDARD 的 data.errorCode 混淆。
分片清单返回 INVOICE_SCHEMA_RETIRED 时重新查询包状态;游标无效时从首次游标请求重新读取。
税务分页确认的 ACK_PAGES_INCOMPLETE 表示尚有页面未读,ACK_MANIFEST_MISMATCH 表示快照或哈希不匹配, ACK_BATCH_MISMATCH 表示已由其他接收批次确认。处理流程见税务分页交付。
code | 含义 | 处理 |
|---|---|---|
INVOICE_DELIVERY_MODE_MISMATCH | 本单未申请该交付模式 | 用申请时选择的模式取数 |
INVOICE_DATASET_NOT_READY | 发票快照未就绪 | 等产品成功后重试 |
INVOICE_DATASET_EXPIRED | 快照已过期 | 联系平台 |
INVOICE_CURSOR_INVALID | 游标不属于本数据集 | 从头重新拉取 |
INVOICE_CURSOR_EXPIRED | 游标令牌过期(默认 24 小时) | 在资料与快照仍有效时省略 cursor 从首请求重读;按新一轮读取去重,不拼接两次结果 |
INVOICE_EXPORT_NOT_FOUND | exportId 不属于本订单 | 重新查询 package/status |
INVOICE_EXPORT_NOT_READY | 分片包未就绪 | 继续查询 package/status |
INVOICE_EXPORT_EXPIRED | 分片包已过期 | 联系平台 |
INVOICE_NOT_REQUESTED | 本单未采集发票 | 用申请时选择的范围取数 |
INVOICE_EXPORT_COUNT_MISMATCH | 分片记录数与快照不一致 | 平台会重建分片包,稍后重查状态 |
INVOICE_EXPORT_PATH_INVALID | 分片包存储路径异常 | 联系平台 |
重试决策
mermaid
flowchart TD
A[收到错误] --> B{哪一层}
B -->|HTTP 401| C[修认证, 不要重试]
B -->|HTTP 409| D[读 code]
D -->|*_NOT_READY| E[等一段时间再重试]
D -->|EXTERNAL_ORDER_CONFLICT| F[换订单号或改回原请求]
D -->|CHANNEL_REQUEST_REPLAYED| G[换新 Nonce 重发]
B -->|HTTP 422| H[修参数, 不要重试]
B -->|业务码 60003| E
B -->|业务码 60002| I[查产品状态定位原因]
B -->|业务码 10001| J{data.errorCode}
J -->|INVOICE_RESULT_NOT_READY| E
J -->|其他汇总处理错误| K[联系平台处理来源或后台任务]
J -->|无处理错误代码| L[核对参数和授权范围]恢复方式
| 场景 | 做法 |
|---|---|
PROCESS 或 60003 | 继续等待同一任务,不要重新创建授权 |
| 平台系统重试 | 沿用 authorization_code 和 job_id,attempt_id 会变 |
| 税局会话失效,授权页要求重新录入凭据 | 使用平台提供的当前授权入口继续,跟踪最新 attempt_id |
TAX_ACCOUNT_RESTRICTED | 先按税局提示解除账号限制,不循环提交密码 |
BROWSER_START_FAILED / BROWSER_CLOSE_TIMEOUT | 属于采集环境恢复,由平台处理,不要求客户反复重新授权 |
INVOICE_COLLECTION_INCOMPLETE | 已取得部分文件但采集区间未完整完成;使用平台提供的重新授权入口,保留旧批次证据 |
INVOICE_DOWNLOAD_QUOTA_EXHAUSTED / INVOICE_EXPORT_QUOTA_EXHAUSTED | 税局额度受限,按平台恢复提示处理;重复授权不能恢复税局当日额度 |
INVOICE_VERIFICATION_POLICY_NOT_APPLIED | 按当前响应区分诊断警告与阻断失败;只有最终失败且要求系统重试时由平台检查节点,不循环提交企业凭据 |
VAT_INVOICE_REVENUE_MISMATCH | 收入核验差异率达到 40%,按 REAUTHORIZE 重新授权采集,不将已有产品可取数解释为整单通过 |
REVENUE_CHECK_ERROR | 收入核验执行异常,按 CONTACT_SUPPORT 联系平台 |
ORDER_FULFILLMENT_FAILED | 采集已完成但本单交付生成失败,由平台处理结果生成,无需重复采集 |
AUTHORIZATION_EXPIRED | 当前未提交授权已到期,重新生成后使用新的授权链接 |
AUTHORIZATION_SUPERSEDED | 链接已被下一代替换,停止使用旧链接 |
| 平台明确创建新授权代次 | authorization_code 会变,按新代次继续处理 |
不要因部分质量警告重建订单
state=SUCCESS 且 qualityStatus=WARNING / collectionResult=COMPLETED_WITH_WARNINGS 表示已有数据可用但覆盖不完整。保存 qualityIssues 与 dataQuality,按业务规则处理; 不要把它当成 60003 循环重试,也不要自动创建新的外部订单。
CONTACT_SUPPORT
平台提示恢复动作 CONTACT_SUPPORT 时停止自动重试, 保留错误码、external_order_id、job_id、attempt_id 联系平台。
哪些请求可以放心重试
重试安全(术语叫「幂等」)的意思是:同一个请求重发多次,结果和只发一次一样, 不会产生重复数据或重复动作。
| 接口 | 重试是否安全 | 说明 |
|---|---|---|
| 创建授权 | ✅ 业务参数一致时 | 比对授权范围、企业、地址等;授权有效秒数和废弃模块筛选不参与已有授权冲突比对,详见创建授权说明 |
| 订单/产品查询 | ✅ | 只读 |
| 数据接口(Key 模式) | ✅ | 只读 |
| 数据接口(签名模式) | ⚠️ 必须换新 Nonce | 复用 Nonce → 409 CHANNEL_REQUEST_REPLAYED |
| V2 游标 | ⚠️ 换新 Nonce;同一 cursor 可重复请求 | 以订单、执行批次、响应 datasetId 和请求游标保存读取进度 |
| V2 分片包状态 | ⚠️ 换新 Nonce;接口本身可安全重复调用 | 会触发/推进准备流程 |
| 文件下载 | ✅ | 仅返回 Accept-Ranges: bytes 的接口支持 Range;普通 HTML 下载不保证支持 |
建议的重试节奏
失败后不要立刻重试:先等一小段时间,每失败一次就把等待时间拉长(术语叫「指数退避」)。
| 错误类型 | 首次等待 | 上限 | 最多次数 |
|---|---|---|---|
60003 / *_NOT_READY | 20 秒 | 5 分钟 | 按业务超时(如 2 小时) |
| 429 | 先遵循 Retry-After,无该头时降低并发并退避 | 按业务超时 | 按业务超时 |
| 5xx / 网络超时 | 2 秒起,每次翻倍;状态轮询仍不少于 15 秒 | 60 秒 | 5 |
| 401 / 422 | 不重试 | —— | —— |
轮询下限
状态查询间隔不得低于 15 秒。更密集的轮询不会让采集更快, 只会增加客户端和服务端负担。税务容量限制返回 429 时,以实际 code 和 Retry-After 为准。
建议留存的排查信息
无论走回调还是轮询,客户端都应把下面这些记录下来:
external_order_id、authorization_code、job_id、attempt_id- 每次请求的
X-Channel-Nonce与响应的code/message - 回调的
event_id与X-RPA-Delivery
排查时把这几项一起提供给平台,可以直接定位到具体采集尝试。