DeepSeek Harness Plugin Hub

Publish and manage complete Harness Profiles. Discover Plugins for your next setup.

Explore

PluginsPresetsDocsNews

Community

Publish a pluginContactReport an issue

Resources

Plugin Hub on GitHubDeepSeek HarnessSystem statusPrivacy notice
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

Independent and unofficial. Not affiliated with, authorized by, or endorsed by DeepSeek.

Request Error Dump — DSH Plugin for DeepSeek Harness
DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in
← Plugins
R

dsh-request-error-dump

Request Error Dump

DSH plugin: when an upstream model API request fails, dump the exact raw request body and the raw upstream error response to disk.

The plugin will be installed here. Keep web if you are unsure.

npx -y @deepseek-ai/dsh plugin --profile web add github:exynos967/dsh-request-error-dump#a8ac6577b897f5ecf366fe2c36d975c6d0ff0b41
READMECompatibilityVersions

Compatibility and provenance

Request Error Dump is published as dsh-request-error-dump and currently resolves to version 0.1.0. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
any
Release source
github
Registry updated
9/22/2026

Versions

0.1.0stable
9/22/2026

Related plugins

Loading related plugins…

Latest
0.1.0
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
Unavailable
Files
Unavailable
Surface
any
License
MIT
Source
github
GitHub
★ 0
Weekly downloads
0
Last push
9/22/2026
View source ↗
README badge

Click the badge to copy Markdown for your README.

Do you maintain this Plugin?Claim benefit · Priority security scan

Verify the GitHub repository declared in package.json to manage this listing. After you claim it, Hub will prioritize a security scan of the current version and publish the result when it passes.

Claim this Plugin →
Report an issue

Related plugins

More verified plugins in developer-tools.

Web App@deepseek-ai/dsh-web-appThe dsh browser-surface bundle: the web patch layer over dsh-base plus the runtime glue plugin (frontend dist serving, web-surface prompt, bash runtime variables, URL line)Sdk Minimal@deepseek-ai/dsh-sdk-minimalThe standalone minimal SDK profile bundle: JSON-RPC, one DeepSeek adapter, persistent shell, and JSONL sessionsSdk App@deepseek-ai/dsh-sdk-appThe dsh SDK profile bundle: stdio JSON-RPC serving and process lifecycle over dsh-baseExperimental Inspector@deepseek-ai/dsh-experimental-inspectorExperimental cross-realm CDP hub for Host debugging and Client Runtime inspection

README

dsh-request-error-dump

DSH 插件:模型请求报错时,把这一次的完整请求体 raw JSON 与上游返回的 raw JSON 一起落盘。

上游 401 / 403 / 429 / 400 到底回了什么,DSH 界面通常只给你一句被归一化过的 "本轮运行失败"。本插件把原始报文留下来,方便事后对照。


它记录什么

每次失败写一个 JSON 文件,包含四块内容:

区块内容
request请求 URL、方法、请求头(默认脱敏)、请求体原文 body.raw 与解析后的 body.json
response状态码、状态文案、响应头、上游响应原文 body.raw 与 body.json
transportError连接被拒、DNS、TLS、超时等传输层异常(此时 response 为 null)
dshDSH 侧上下文:turn / step / provider / failure.{message,code,status,requestId} / agent 与会话 id / correlation(本次配对靠哪条信号,见下)

真实样例(截断):

{
  "schema": "dsh-request-error-dump/v1",
  "kind": "http-error",
  "capturedAt": "2026-09-22T05:27:46.919Z",
  "durationMs": 15,
  "request": {
    "url": "https://api.deepseek.com/chat/completions",
    "method": "POST",
    "headers": { "content-type": "application/json", "authorization": "<redacted:30 chars>" },
    "body": {
      "present": true,
      "bytes": 83,
      "truncated": false,
      "raw": "{\"model\":\"deepseek-chat\",\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}],\"stream\":true}",
      "json": { "model": "deepseek-chat", "messages": [{ "role": "user", "content": "hi" }], "stream": true },
      "source": "string"
    }
  },
  "response": {
    "status": 401,
    "statusText": "Unauthorized",
    "headers": { "content-type": "application/json", "x-request-id": "a1b2c3d4" },
    "body": {
      "present": true,
      "bytes": 130,
      "truncated": false,
      "raw": "{\"error\":{\"message\":\"Authentication Fails, Your api key is invalid\",\"type\":\"authentication_error\",\"code\":\"invalid_request_error\"}}",
      "json": { "error": { "message": "Authentication Fails, Your api key is invalid", "type": "authentication_error", "code": "invalid_request_error" } }
    }
  },
  "transportError": null,
  "dsh": {
    "turn": 4,
    "step": 1,
    "provider": "deepseek-official",
    "agent": { "id": "main", "sessionId": "main-session-abc" },
    "failure": { "message": "Authentication Fails", "code": "AUTH", "status": 401, "requestId": "a1b2c3d4" }
  }
}

body.raw 是逐字节的原文,body.json 只是同一份文本的解析视图,方便直接阅读。 用 bodyFormat: raw 可以只留原文,用 bodyFormat: parsed 可以只留解析结果。


开箱即用:默认只抓模型请求

零配置。装好重启后,只有模型(LLM)请求失败才会 dump 到 ~/.dsh/request-error-dump/。

怎么判定"是模型请求"

两条信号,取并集(宁可多抓,不可漏抓):

  1. 权威信号 —— agent/request-error 认领:DSH 循环在每次模型请求尝试失败时派发该事件。 被认领 = 确定是模型请求,无论 URL 长什么样;
  2. 兜底信号 —— 请求结构:POST 且 JSON body 里带字符串 model / modelId, 或 URL 命中少数"模型写在路径里"的形态(Gemini :generateContent、Bedrock /model/<id>/converse)。这一条用来救不走 agent 循环的模型调用(比如会话标题生成)。

为什么不用 URL 白名单?因为会静默漏抓——自定义网关的路径五花八门,调试工具最怕漏。 所以这里刻意反过来:URL 只列了少数几类,其余全靠 body 里的 model 字段和事件认领。

两个时序细节

  • 不是瞬间落盘:默认延迟 flushDelayMs: 1500(1.5 秒)。这个窗口用来等 agent/request-error 来认领并补上 turn/step/provider/failure;窗口内没被认领、 又不像模型请求的,直接丢弃(这就是过滤噪音的机制)。设 0 则立即写、不等上下文;
  • 只抓走到 HTTP 的失败:密钥没配、路由没有适配器(NO_ADAPTER)这类发请求之前 就失败的错误不会产生 dump。

默认排除的端点

excludeUrls 默认含 ['/user/balance']:DSH 桌面版的余额组件每分钟轮询一次它, 密钥失效时一分钟一个 401,50 个保留位约 50 分钟就被占满。模型门禁本来就会挡掉它, 这里再列一次是双保险——万一你设了 modelRequestsOnly: false,它仍然不会被记录。

- id: request-error-dump
  config:
    excludeUrls: ['/user/balance']   # 默认值,可按需增删

为什么监听器要 prepend

agent/request-error 是 waterfall:不调用 next() 的监听器会否决整条链。 dsh-llm-retry 在接管重试时正是返回 { kind: 'retry' } 而不调 next()。 如果本插件按默认顺序注册在它之后,被重试的失败就永远轮不到我们记录。 所以监听器用 { prepend: true } 注册在最前,自己再 return next() 把控制权交回去—— 既不会漏,也不会干扰别人的恢复逻辑(已用真实 Cordis 跑过否决场景验证)。

为什么必须在 wire 层做

DSH 的适配器抛出的错误会被 normalizeLlmFailure 归一化,只保留 message / code / status / providerRetryAfterMs / requestId —— 上游响应原文在这一步就 被丢掉了。所以任何只挂在 agent/request-error 或 llm/stream 上的观察者都拿不到 raw 报文。

真正还同时存在"请求体原文"和"上游响应原文"的地方,只有适配器发起 HTTP 请求的那一刻。 本插件因此包装进程全局的 fetch:

  • 成功路径完全透明:2xx 直接原样返回,响应体一个字都不读,零开销;
  • 失败路径:非 2xx 时先 response.clone() 再读取,调用方自己的响应体仍然可读; 传输层抛错时记录异常并原样重抛;
  • 捕获回调里的任何异常都会被吞掉——记录失败绝不等于请求失败。

安装

本插件零运行时依赖,可直接从目录安装。

# 从 GitHub 安装(推荐)
dsh plugin --profile desktop add github:exynos967/dsh-request-error-dump

# 本地目录(开发用)
dsh plugin --profile desktop add link:D:/dsh-workdir/test/dsh-request-error-dump

装完需要重启 DSH(dsh.profile.bundles 在启动时读取)。

版本提醒:本插件已在 Cordis 4.0.2(DSH 桌面版内置的 @deepseek-ai/cordis)上 完成挂载验证。它只使用 ctx.on、ctx.effect 与进程全局 fetch,不导入任何 @deepseek-ai/* 包,因此不挑 profile,也不受 DSH 小版本升级影响。


配置

所有配置项都有可用默认值,不配也能跑。写在 profile 的 cordis.patch.yml 里覆盖该行:

- id: request-error-dump
  config:
    outDir: ~/.dsh/request-error-dump
    maxFiles: 50
    maxCaptureBytes: 0
    bodyFormat: both
    redactHeaders: ['authorization', 'api-key', 'cookie']
    flushDelayMs: 1500
    ignoreAborted: true
    modelRequestsOnly: true
    excludeUrls: ['/user/balance']
    log: true
选项默认值说明
outDir$DSH_HOME/request-error-dump(即 ~/.dsh/request-error-dump)落盘目录,支持 ~
maxFiles50保留最新的 N 个 dump,更早的自动清理;0 表示不清理
maxCaptureBytes0单个体积上限(字节);默认 0 = 不截断,保持完整。截断时会标记 truncated: true 并跳过 json 视图
bodyFormatbothraw 只留原文 / parsed 只留解析结果 / both 都留
redactHeadersauthorization、proxy-authorization、api-key、x-api-key、x-auth-token、x-goog-api-key、cookie、set-cookie要脱敏的请求头/响应头名;设为 [] 即完全原样
flushDelayMs1500延迟落盘窗口,用来等 agent/request-error 补上 DSH 上下文;0 表示立即写、不等上下文
modelRequestsOnlytrue只记录模型请求。true 时:被 agent/request-error 认领的,或请求结构像模型调用的(POST + body 带 model/modelId,或 URL 命中 Gemini/Bedrock 形态)才写;其余在窗口结束时丢弃。设 false 恢复"抓所有失败的 HTTP"
excludeUrls['/user/balance']正则列表,对 "METHOD URL" 求值,命中即不记录(在模型门禁之前生效)。设为 [] 取消默认排除
ignoreAbortedtrue用户主动取消(AbortError)不记录
logtrue每次落盘在 host 日志里打一行提示

落盘与保留

  • 文件名形如 2026-09-22T05-27-46-919Z-0001.json,时间戳前缀保证字典序即时间序;
  • 先写 .tmp 再 rename,不会出现半截文件;
  • 超过 maxFiles 后按时间从旧到新清理;并发写入的清理串行化,不会误删新文件;
  • 插件卸载/重载时会把还在等待窗口里的记录刷完,不会丢最后一次报错。

⚠️ 隐私边界

请求体里就是你的完整上下文:系统提示词、源码、工具结果、对话历史,可能还有你贴进去的 密钥。插件默认只脱敏请求头里的凭据,正文一律原样保存。

  • 别在不可信机器上长期开启,也别把 outDir 指向同步盘;
  • 分享 dump 给他人或提交 issue 前请人工检查;
  • 想连请求头也原样保留,设 redactHeaders: [](危险,默认不这么做)。

它不做什么(诚实边界)

  • "模型请求"的判定不是 100% 精确:包装层是进程全局的,判定靠事件认领 + 请求结构。 极端情况下(自定义网关路径 + body 里没有 model 字段 + 该调用又不走 agent 循环) 可能漏抓,此时把 modelRequestsOnly 设为 false 可退化为全抓;
  • 不抓 provider 原生 HTTP 报文的全貌:抓到的是适配器真正 fetch 出去的那一份 body 与真正回来的那一份响应体,但不含 DNS/TLS 层信息、重定向链、以及适配器内部的 重试次数;
  • 不抓流中途失败:HTTP 200 之后 SSE 流中断/解析失败不会触发落盘(那需要缓冲整条 流,代价太高);
  • 不抓 FormData / Blob / ReadableStream 请求体:这些 body 一旦读取就被消费,dump 里 会标记 unavailable 说明原因;
  • dsh 区块的关联是启发式的:按 requestId → 状态码+模型形态 → 模型形态 → 状态码 → 最近一条 的顺序配对,并把实际命中的那条信号写进 dsh.correlation (requestId / status+model / model / status / newest),不假装确定。 命中 newest 时说明只是兜底猜测;dump 里的 URL 与时间戳始终是准的;
  • 不改动任何请求:纯观察者,不重试、不修改、不注入。

工作原理

监听器注册在插件自己的上下文上(无 scope 标记)。核对过 @deepseek-ai/dsh-scope 的 scopeTarget 过滤器:scopeOf(ctx) === undefined 时一律放行,因此根级监听器能收到 每个 agent 的 agent/request-error,不需要 inject 任何服务。同时 agent/request-error 是 waterfall —— Cordis 明确规定"不调用 next() 的监听器会否决整条链",所以本插件 永远 return next(),绝不干扰 dsh-llm-retry 这类拥有恢复权的监听器。

适配器 fetch(init.body = JSON.stringify(payload))
        │
        ▼
globalThis.fetch 包装层          ← 只在此处两段原文同时存在
        │  2xx → 原样返回(不读 body)
        │  非 2xx / 抛错 → 克隆响应、组装 record
        ▼
dump-store 待写队列(flushDelayMs 窗口)
        │  agent/request-error 到达 → 按 requestId/status 认领并补上 dsh 区块
        ▼
原子写入 $DSH_HOME/request-error-dump/*.json → 按 maxFiles 清理

模块划分(每个文件一个职责):

文件职责
index.jsCordis 插件入口:配置合并、装配、卸载
src/paths.jsDSH_HOME / ~ 路径解析
src/record.js请求头归一化与脱敏、body 读取/截断/解析、record 组装
src/dump-store.js待写队列、关联认领、原子写入、保留清理
src/fetch-capture.jsglobalThis.fetch 包装层(可重复安装、可还原)
src/annotate.jsagent/request-error 监听器(纯观察者,始终 return next())

测试

npm test                      # 32 个单元/端到端用例,零依赖
node test/cordis-integration.mjs   # 在真实 @deepseek-ai/cordis 上挂载验证

cordis-integration.mjs 会在本机找到 DSH 安装目录时,用真实 Cordis 运行时加载插件、 触发一次真实 waterfall 派发并校验 dump 内容;找不到时自动跳过。可用 DSH_APP_ROOT 指定安装路径。


License

MIT