Skip to main content
参考

问题排查

按观察到的现象或报错原文定位原因与解决办法。

本页按观察到的现象索引。如果已经拿到错误码、异常类名或进程退出码,请直接查阅错误与错误码,该页按错误身份索引。

会话始终无法启动

现象: query() 没有产出任何消息,或首个 await 长时间挂起后抛出异常。
原因如何确认解决办法
qoderclicn 可执行文件不在 PATH 中异常为 QoderCliProcessError,且提示信息中包含可执行文件设置 pathToQoderCLIExecutable,或导出 QODERCLI_PATH
启动超出加载超时每次都在固定时长后失败提高 loadTimeoutMs;磁盘较慢时的冷启动可能超过默认值
未配置凭证异常带有 code: 'auth_not_configured'传入 auth,参见认证
凭证环境变量未设置code: 'auth_access_token_env_var_not_configured' 或 ..._service_account_...导出认证辅助函数读取的那个变量
找不到 CLI(Java)CliNotFoundExceptionqoderCliPath 指向的路径无效时修正路径;当前平台不在托管范围(例如 Windows arm64)时,用 qoderCliPath 或 QODERCN_PATH 指定已安装的 CLI
托管 CLI 安装失败(Java)RuntimeInstallException见托管 CLI 无法安装
临时目录不安全(Java)UnsupportedSecureSecretTransportException见启动报 UNSUPPORTED_SECURE_SECRET_TRANSPORT
凭证环境变量未设置(Java)AuthenticationException,消息含 not set在启动 JVM 前导出该变量;putEnvironment() 传入的值不参与认证
排查启动问题时建议捕获 stderr,其中通常直接指明了真实原因:
options: {
  stderr: (data) => process.stderr.write(data),
}
Java 没有 stderr 回调。CLI 非正常退出时,CliProcessException.getStderrTail() 返回有界且已脱敏的 stderr 尾部;启动和关闭过程的事件用诊断监听器收集,见收集可用于上报的诊断信息。

选项在进程启动前即被拒绝

部分选项会在前置阶段校验,因此拼写错误会立即失败,而非让某项功能被静默关闭。以下异常在 QoderCN CLI 启动之前同步抛出。
报错信息原因解决办法
securityScan contains unknown option: <key>
security_scan contains unknown option: <key>.
开关名拼写错误TypeScript 使用 l1StaticCheck / l2LightweightScan / l3DeepScan;Python 使用 l1_static_check / l2_lightweight_scan / l3_deep_scan
securityScan.<key> must be a boolean
security_scan.<key> must be a bool.
传入了 'yes' 之类的字符串传入 true 或 false
security_scan must be a dict.Python 侧传入了非映射类型传入开关组成的 dict
native mode requires at least one enabled scope; use memory: {} to disable SDK memory
native memory requires at least one enabled scope; use memory={} to disable
projectScope 与 userScope(Python 为 project_scope 与 user_scope)同时设为 false保留一个作用域,或用 memory: {}(Python 为 memory={})关闭记忆
[createSdkMcpServer] Server name must be a non-empty string.name 缺失或为空为服务指定名称
[createSdkMcpServer] Tool name must be a non-empty string in server "<server>".定义工具时未提供名称为每个工具命名
[createSdkMcpServer] Tool "<tool>" must have a non-empty description string.缺少 description补充描述;模型依赖它判断何时调用该工具
Java 的 QoderClient.Builder.build() 和 SessionOptions.Builder.build() 同样在启动前校验,失败时同步抛出 InvalidOptionsException,例如没有设置 auth,或同时设置了 resume 和 continueSession。完整规则见 SessionOptions.Builder。

SDK 与运行时版本不匹配

现象: ProtocolVersionMismatchError 或 UnsupportedCliCapabilityError(Java 为 ProtocolVersionMismatchException 或 UnsupportedCliCapabilityException)。 这表示 SDK 与 QoderCN CLI 来自不兼容的版本,通常出现在运行时由使用方单独管理、而非使用包内自带运行时的情况下。
try {
  for await (const message of query({ prompt: 'ping' })) { /* ... */ }
} catch (error) {
  if (error instanceof UnsupportedCliCapabilityError) {
    console.error(`runtime lacks capability: ${error.capability}`);
  }
}
UnsupportedCliCapabilityError.capability 会指明缺失的具体能力,据此可判断应升级运行时还是停用该选项。解决办法是升级 QoderCN CLI,或移除 pathToQoderCLIExecutable 覆盖,改用包内自带的运行时。 Java SDK 默认使用托管的锁定版本 CLI。出现不匹配时,检查是否通过 qoderCliPath 或 QODERCN_PATH 指定了其他版本;移除后 SDK 改用托管 CLI。UnsupportedCliCapabilityException.capabilityState() 返回 CLI 对该能力的声明状态。

Agent 始终不调用自定义工具

现象: 工具已注册、会话正常启动,但 Agent 绕过该工具完成了任务。 按以下顺序排查:
  1. 确认完整工具名。 服务名为 my_tools、工具名为 greet 时,模型看到的是 mcp__my_tools__greet。白名单必须使用完整名称。
  2. 确认未被白名单排除。 若设置了 tools 或 allowedTools 但未包含该工具,模型不会看到它。参见工具命名与白名单。
  3. 确认懒加载影响。 启用 MCP 懒加载后,工具不会全部常驻。对必须始终可用的工具做标记:
tool('greet', 'Say hello', { name: z.string() }, handler, { alwaysLoad: true })
  1. 检查工具描述。 描述是模型判断该工具适用场景的唯一依据。「做某件事」这类描述会被忽略,应明确写出触发条件。

Agent 在本应有权限的操作上停住

现象: 工具调用被拒绝,或运行停滞在一个永不到来的审批上。
原因解决办法
disallowedTools 覆盖了该工具disallowedTools 的优先级高于 allowedTools 和 permissionMode
非交互任务中缺少审批处理设置不需要交互提示的 permissionMode,或提供 canUseTool
同时设置了 canUseTool 与 permissionPromptToolName两者互斥,只能择一
完整的优先级顺序参见权限控制。

模型选择回调超时

现象: ModelPolicyTimeoutError。Java 不抛出异常,诊断监听器收到 inbound_control_request_failed 事件,detail 含 error=MODEL_RESOLUTION_TIMEOUT;本轮结果按 ResultMessage.outcome() 处理。 resolveModel 位于每次请求的关键路径上,默认预算为 500 毫秒。若回调中查询数据库或调用 HTTP 服务,将超出该预算。
options: {
  resolveModel: async (context) => ({ model: pickModel(context) }), // 保持本地计算
  resolveModelTimeoutMs: 1_500,                                     // 或提高预算
}
建议在回调之外缓存决策结果,而非反复提高超时时间。这里的每一毫秒都会叠加到每一轮请求上。 Java 的预算用 SessionOptions.Builder.resolveModelTimeoutMs(long) 调整,默认同为 500 毫秒。超时后请求该模型的轮次失败,不会回退到其他模型。

记忆未被写入

现象: Agent 已获得新知识,运行结束后却没有任何内容被保存。 生成在轮次结束之后由后台 Agent 执行。若进程在收到最终 result 消息后立即退出,可能中断正在进行的写入。
// 退出前等待未完成的生成
await q.flushMemory();
Python 未使用 daemon 选项时,单次 query() 在结束输入前自动 flush,QoderSDKClient 在 disconnect() 或退出上下文时自动 flush;自动 flush 失败只记录日志,需要确认成功时显式调用 flush_memory()。使用 daemon 选项且 CLI 声明了 daemon_async_session_teardown_v1 时,两者都不自动 flush,关闭完成不代表记忆已经写完;关闭后要立即读取记忆文件或启动下一个会话时,改用 QoderSDKClient,在关闭前调用 await client.flush_memory()。参见在运行时控制记忆。 Java 的一次性查询在 ResultMessage 之后还可能收到 Memory 消息,应把消息流读到结束再关闭。 若 flushMemory() / flush_memory() 已返回而文件仍无变化,请检查生成结果:no_change 和 skipped 属于正常结果,并非失败。参见查看记忆的执行结果。

在设置中开启的功能未生效

现象: 设置文件中已开启某项功能,但会话表现为未开启。 部分查询选项具有最终决定权:一旦传入,它会替换而非合并对应的设置块,未显式列出的开关全部按 false 处理。 securityScan 即属于此类。若 settings.json 开启了 l1StaticCheck,而查询传入 securityScan: { l3DeepScan: true },则本次会话的 l1StaticCheck 为关闭状态。需要保留的开关必须逐个重新声明。参见该选项覆盖设置,而非合并。

Agent 遵守了未曾设置的规则

现象: Agent 遵循了提示词中并不存在的约定。 运行 QoderCN CLI 的机器上的指令文件默认会被加载,且这些文件可以引入其他文件。可通过日志确认实际加载了什么:
options: {
  hooks: {
    InstructionsLoaded: [{ hooks: [async (input) => {
      console.log(input.memory_type, input.file_path, input.load_reason);
      return {};
    }] }],
  },
}
若需在不同主机上获得可复现的运行结果,使用 settingSources: [] 不从磁盘加载任何文件。参见从文件系统加载指令。

Java 运行环境问题(Java)

以下问题只出现在 Java SDK。

托管 CLI 无法安装

现象: 启动或 installRuntime() 以 RuntimeInstallException(错误码 RUNTIME_INSTALL_FAILED)失败。SDK 不会改用其他版本的 CLI。
原因解决办法
运行环境不能访问 Qoder 下载站点在构建镜像或部署阶段预热缓存:java -cp qodercn-agent-sdk-VERSION.jar com.qoder.sdk.RuntimeInstaller(VERSION 为应用依赖的 SDK 版本);或随应用携带锁定的压缩包,用 ManagedRuntimeOptions.Builder.localArchive(Path) 指定
需要经过代理下载按 JVM 进程环境中的 HTTPS_PROXY、ALL_PROXY 和 NO_PROXY 选择代理,在启动 JVM 前设置
只能访问企业镜像用 ManagedRuntimeOptions.Builder.baseUrl(URI) 指向镜像,镜像按 <CLI 版本>/<文件名> 的布局提供相同的文件
禁止下载(DownloadPolicy.DISALLOW 或 --no-download)且缓存为空先预热缓存,或提供本地压缩包
压缩包大小或 SHA-256 与锁定值不符镜像或本地文件不是本 SDK 版本锁定的那一个。换成正确的文件,SDK 不提供跳过校验的选项
RuntimeInstaller 的参数和各项配置见预热与离线。

启动报 UNSUPPORTED_SECURE_SECRET_TRANSPORT

现象: 启动以 UnsupportedSecureSecretTransportException 失败,CLI 没有运行。 SDK 通过只有当前账号可读的临时文件把认证和 MCP 配置交给 CLI。java.io.tmpdir 或它的某一级上级目录允许其他账号在其中改名或替换条目时,SDK 拒绝启动,不会改用启动参数或环境变量传递凭据。只有当前账号可写的目录和带 sticky 位的公共目录(如 Linux 的 /tmp)满足要求。 解决办法:用 JVM 启动参数 -Djava.io.tmpdir=<dir> 指向只有当前账号可写的目录。

Windows 上自行指定的 .cmd CLI 拒绝启动

现象: Windows 上启动以错误码 CLI_START_FAILED 失败,消息含 refusing to launch a Windows batch-file CLI。 自行指定的 CLI 是 .cmd 或 .bat(例如 npm 安装的 qoderclicn.cmd),而 CLI 路径或启动参数含有 cmd.exe 会解释的字符,包括括号(如工具规则 Bash(git:*))。cmd.exe 可能改写这些参数,SDK 因此拒绝启动。 解决办法:不指定 CLI 路径,使用 SDK 托管的 CLI;或把 qoderCliPath 指向 .exe。自动查找时,同一目录中的 .exe 优先于 .cmd。

用诊断事件定位关闭与清理问题

会话关闭后仍有残留时,SDK 通过诊断事件报告,不会抛出异常。注册方式见收集可用于上报的诊断信息。
事件 type含义处理方式
private_directory_cleanup_failed认证或 MCP 配置用的私有临时目录没有清理完成。detail 含 credential files may remain 时,凭据文件可能仍在磁盘上按 detail 中的路径检查并删除残留目录
cli_process_alive_after_close关闭流程结束后 CLI 进程仍在运行,detail 列出已执行的终止步骤在宿主侧确认并结束残留进程
process_tree_cleanup_degraded当前环境无法完整清理 CLI 进程树CLI 启动的子进程可能残留,在容器或进程组层面兜底清理
capability_downgraded已通过能力检查的控制请求在运行时被 CLI 判为不支持,capability 给出能力名之后调用该功能会以 UnsupportedCliCapabilityException 失败。检查是否自行指定了其他版本的 CLI
mirror_drain_timeout关闭时外部会话存储的写入没有在时限内完成部分记录可能没有写入存储。检查存储后端的延迟,需要时调大 SessionMirrorOptions 的 closeDrainTimeoutMs
完整的事件列表见 DiagnosticEvent。

收集可用于上报的诊断信息

需要升级排查时,请在现场信息丢失前收集以下内容:
options: {
  debug: true,
  debugFile: '/tmp/qoder-sdk-debug.ndjson',
  stderr: (data) => process.stderr.write(data),
}
Java 的诊断监听器可以注册在客户端或单个会话(SessionOptions.Builder.diagnosticsListener)上;会话设置了监听器时只用会话级。托管 CLI 的 managed_runtime_* 事件只发给客户端级监听器。诊断是尽力交付的,不能当作无损审计日志。CLI 非正常退出时,同时记录 CliProcessException.getExitCode() 和 getStderrTail()。 同时记录 SDK 版本、system/init 消息中的 QoderCN CLI 版本、session_id,以及 Result 的 subtype 和可能存在的 error_code。除诊断流程明确需要,凭证、提示词和源码内容应做脱敏处理,参见日志与问题反馈。

下一步