微信接口错误码速查
同步过程中任一步失败,失败原因会在文章列表的红色微信 Logo 上悬停显示,内容为插件请求微信后的原始结果(含 errcode / errmsg),服务端日志中也会记录。本节按插件实际调用的 7 个接口整理微信官方给出的限制与接口级错误码(其中 material/get_material 只用于素材复用前的校验、draft/get 只用于重复同步前校验已有草稿,它们出错不会让同步失败:前者保守重传封面、后者按「草稿不存在」处理改为新建草稿,判定规则见下方单独一节),再汇总通用(全局)错误码在本插件中的常见诱因,最后补充未出现在全局返回码表 / 各接口文档只一笔带过、但在同步场景里真实会遇到的错误码,以及非 errcode 类失败,便于对照排查。
各接口的官方限制与接口级错误码
| 接口 | 关键限制 | 官方文档明确列出的错误码 |
|---|---|---|
GET /cgi-bin/token | access_token 有效期 7200 秒;AppSecret 必须正确且未被冻结;调用方出口 IP 必须在公众号 IP 白名单内 | -1、40001、40002、40013、40125、40164、40243、41004、50004、50007 |
POST /cgi-bin/media/uploadimg | 仅支持 jpg / png,单张必须 < 1 MB;上传的图片不占用素材库 10 万配额 | 40005、40009 |
POST /cgi-bin/material/add_material?type=image | 支持 bmp / png / jpeg / jpg / gif,必须 < 10 MB;永久图片素材数量上限 100,000 | 40007(其余走通用错误码) |
POST /cgi-bin/material/get_material | 只能用本公众号的永久素材 media_id 查询(临时素材、其它公众号的 id 均无效);图片类素材返回图片二进制流、图文/视频才返回 JSON,故「响应不是 JSON」正是素材存在的标志;官方文档的错误码小节只列出右列 3 个 | -1、40001、40007 |
POST /cgi-bin/draft/add | title ≤ 64 字、author ≤ 8 字、digest ≤ 120 字、content 需 < 2 万字符且 < 1 MB、content_source_url ≤ 1 KB;thumb_media_id 必须是本公众号的永久图片素材 id;正文图片 URL 必须来自 uploadimg,外部图片会被过滤。长度口径以实测为准:接口文档把 title 写作 32 字、author 写作 16 字,但平台与编辑器口径为标题 64 字、作者 8 字,服务端也按后者校验(作者超 8 字会返回未收录的 45110),插件按 64 / 8 / 120 截断 | 53404、53405、53406(商品/带货能力相关,插件不使用);其余走通用错误码 |
POST /cgi-bin/draft/get | 只能用本公众号的草稿 media_id 查询(永久素材、其它公众号的 id 都无效,会返回 40007);草稿仍在时返回带 news_item 的草稿详情 | -1、40001、40007(插件只把「拿到 news_item」当「草稿仍在」,其余一律按不存在处理,见下方说明) |
POST /cgi-bin/draft/update | 与 draft/add 同样的字段限制(title / author / digest / content 上限一致),另须带 media_id 与 index;请求体的 articles 是单个对象(draft/add 是数组) | 同 draft/add:53404、53405、53406(插件不使用);其余走通用错误码 |
字段长度以实测为准(接口文档与实际校验不一致):插件会主动截断为 标题 → 64 字、作者 → 8 字(插件设置的「默认作者」与回退的文章作者同样适用)、摘要 → 120 字。
计字统一采用公众号编辑器口径(与编辑器的标题 / 作者 / 摘要计数器一致,实测可准确截到微信允许的长度):一个汉字 / 全角字符计 1 个字,一个半角字符(英文字符、数字、符号)计 0.5 个字,一个 emoji 等增补字符计 2 个字;按码点整体取舍,不会把 emoji 截成半个代理对。
注意接口文档写的上限偏大 / 偏小都可能踩坑:文档称
author≤ 16 字,但服务端按平台规则(作者名 8 字)校验——按 16 字提交会被拒绝并返回未收录的45110 author size out of limit;文档称title≤ 32 字,而编辑器与平台口径是 64 字。只要有字段因超长被截断:预览弹窗会在该字段旁显示「已截断」标识、底部给出长度检查结论(并可在预览中直接看到截断后的值),服务端日志同时输出
warn(字段名、原始长度、上限与截断后的值),便于核对实际提交到草稿的内容。
素材校验接口 material/get_material 的判定规则
该接口只用于判断缓存里的封面 media_id 还能不能复用(见工作原理的「素材缓存」),因此插件把它的响应分成三态处理,任何一态都不会让同步失败:
| 响应 | 判定 | 插件的处理 |
|---|---|---|
| 2xx 且响应体不是 JSON(图片二进制流) | 素材仍在 | 复用缓存里的 media_id,不重复上传 |
JSON 中 errcode 为 40007 | 素材已被删除(素材在公众号后台被清理) | 重新上传一份封面并覆盖缓存 |
其余全部:-1(系统繁忙)、40001 / 40014 / 42001(凭证失效/过期)、45009 / 45011(额度、频控)、代理未转发返回的 404/HTML、网络异常、空响应体 | 给不出结论 | 保守重传并覆盖缓存(日志记「校验给不出结论(代理未转发该接口 / 限流 / 网络异常等)」) |
关键点:
- 只有明确的
40007才判「已删除」,因为「响应不是 JSON」本身就代表成功拿到素材本体——图片类是二进制流,判「素材还在」不能靠 JSON 字段。校验请求只读取响应体头部 256 字节即中止传输,不会把整张封面重新下载一遍。- 没有结论时选择重传:多传一份素材的代价(永久素材占配额)远小于复用一个已失效的
media_id(整篇草稿被40007拒绝、发不出去),因此这类日志属正常降级;若它每次同步都出现,首要排查「接口地址」代理是否放行了/cgi-bin/material/get_material(详见接口地址与反向代理),其次看是否45009/45011限流或凭证失效。- 与正文图片的地址校验不同:正文图片只用
HEAD直连它的微信图片地址(不经代理),而本接口与其他微信接口一样发往插件设置的「接口地址」(留空则直连官方),因此需要被代理转发。
草稿校验接口 draft/get 的判定规则
仅在开启「重复同步更新草稿」时才会调用(见配置):该开关关闭时插件不做任何校验,每次同步都直接新建草稿(draft/add),本节与 draft/update 都不参与。除同步的执行阶段外,MCP 的 wechat_sync_submit 也会在提交时调用它一次,用来判断本次「预计」是更新还是新建(还在 → 预计 update,已不在 → 预计 create),避免草稿已删除时仍报「将更新」;该判断只写进提交结果的说明文案,不作为返回字段(实际动作由 wechat_sync_status 的 draftAction 给出)。
该接口只用于判断「上次同步写入的那份草稿还在不在」,据此决定这次是更新还是新建,因此任何拿不到确定结论的情况都按不存在处理,不会让同步失败:
| 响应 | 判定 | 插件的处理 |
|---|---|---|
2xx 且 JSON 里带 news_item(草稿详情) | 草稿仍在 | 调用 draft/update 更新这份草稿(media_id 不变) |
JSON 里 errcode 为 40007(草稿已被删除,或 media_id 不属于本公众号) | 草稿已不存在 | 调用 draft/add 新建一份草稿,并把新的 media_id 记到该文章的任务记录上 |
其余全部:-1(系统繁忙)、40001 / 40014 / 42001(凭证失效/过期)、45009 / 45011(额度、频控)、代理未转发返回的 404/HTML、网络异常、空响应体 | 给不出结论 | 同样按不存在处理、直接新建草稿(日志记「校验草稿是否存在…按『不存在』处理,改为新建草稿」) |
关键点:
- 判错方向的取舍:误判「不存在」的代价只是多出一份草稿(可在公众号后台删除,且下次同步不会再命中它);误判「存在」会让
draft/update失败、整次同步发不出去。因此只有明确取回草稿详情才算「存在」。- 更新失败的处理:
draft/update被40007拒绝(草稿已被删除,或草稿引用的封面素材已失效)时改为新建草稿;封面若确实失效,新建路径会再触发一次封面重传(见下方「素材缓存」与retryWithFreshCover)。与素材失效无关的更新失败(如45011频控)照原样报错,不做无谓重试。- 代理未转发该接口:
draft/get拿不到结论 → 按「不存在」处理 → 每次都新建草稿(同步照常成功,只是草稿箱里会多出重复草稿)。若日志里每次同步都出现该提示,先检查「接口地址」代理是否放行了/cgi-bin/draft/get(详见接口地址与反向代理)。- 校验本身不算「写操作」:
draft/get是只读的,未转发它不会报错;但只要转发了draft/get却没转发draft/update,就会出现「草稿被判定为仍在 → 更新失败」的报错,因此建议两个路径一起放行。