排障手册
面向运维的"症状优先"手册。每一条都给出应查看的信号(审计事件、 accounts list --details 的可用性取值、或 HTTP 状态码与 error.code)、 原因与处置办法。涉及的配置项在 CONFIGURATION.zh-CN.md; 流内错误码定义见 STREAM_ERROR_CONTRACT.md。
信号在哪里
审计日志。 stderr 上每行一个 JSON 对象。
log_level(默认info)是 输出的最低级别;debug会开启可选的request_shape诊断事件。字段只包含 计数、布尔值、枚举标签以及 16 位十六进制的auditHash(account_hash、conversation_hash、detail_hash);日志绝不携带 提示词正文、工具参数、令牌或签名。kiro-provider accounts list --details(或--json)。AVAILABILITY列是每个账号在选择器眼中的状态:取值 含义 available健康、未限流、额度未耗尽。 rate-limited429/退避窗口生效中,持续到RECHECK_AT。quota-exhaustedKiro 报告额度已用完;到期探测确认新周期后自动回池。 overage-blocked健康且额度未耗尽,但付费超额次数超过 overage_threshold,被stop_on_overage排除。unhealthy因临时原因被标记不健康( HEALTH为unhealthy)。needs-reloginrefresh token 或 OIDC 客户端已永久失效;只有 accounts relogin能恢复。账号较多时,
--sort availability会按上表顺序排列(最不可用的排在最后),--sort usage --order desc则把最接近额度上限的账号排到最前。HTTP 状态码与
error.code。 OpenAI 形态的路由返回{ "error": { "type", "code", "message" } };/v1/messages返回 Anthropic 信封,其中额度402会映射为429 rate_limit_error,Provider 的错误码 保留在 message 文本中。
systemd 用户服务的 journalctl 查询模板
审计日志就是服务的 stderr,因此 journal 就是日志。-o cat 只输出消息体, 每一行都是可解析的 JSON。
# 最近 200 行,便于人工阅读
journalctl --user -u kiro-provider.service -n 200 --no-pager
# 最近一小时的 warn / error
journalctl --user -u kiro-provider.service --since -1h -o cat --no-pager \
| grep -E '"level":"(warn|error)"'
# 实时跟踪某一类事件
journalctl --user -u kiro-provider.service -f -o cat \
| grep --line-buffered -F '"event":"sdk_stream_terminal"'
# 按账号哈希统计令牌刷新失败(需要 jq)
journalctl --user -u kiro-provider.service --since -1d -o cat --no-pager \
| grep -F '"event":"account_token_refresh_failed"' \
| jq -r '[.timestamp, .account_hash, .error_code, .refresh_token_dead] | @tsv'
# 统计当天各种终止来源的数量
journalctl --user -u kiro-provider.service --since today -o cat --no-pager \
| grep -F '"event":"sdk_stream_terminal"' \
| jq -r '.terminal_provenance' | sort | uniq -c
# 查看某个账号的全部事件(`accounts list --json` 里的 id 不是审计哈希;
# 从任意提到该账号的事件里复制 `account_hash`)
journalctl --user -u kiro-provider.service --since -1d -o cat --no-pager \
| grep -F '"account_hash":"0123456789abcdef"'不使用 systemd 时,启动 kiro-provider serve 时把 stderr 重定向到文件,再 用同样的 grep/jq 过滤即可。
账号与额度
账号显示 needs-relogin;日志出现 account_token_refresh_failed
- 查看:
accounts list --details的AVAILABILITY为needs-relogin; 审计事件account_token_refresh_failed(warn),其中refresh_token_dead: true,error_code形如invalid_grant、InvalidGrantException、ExpiredTokenException、InvalidTokenException; 后台维护轮次会以account_maintenance_token_refresh_failed记录同一状况。 - 原因: Kiro 的令牌服务拒绝了 refresh token 或 OIDC 客户端注册本身。 access token 类错误(
bearer token ... is invalid)不是永久性的, 会通过强制刷新处理;只有 refresh-dead 标记才会停用账号。 - 处置:
kiro-provider accounts relogin <id|email>。账号保留内部 ID 与会话亲和记录。NETWORK_ERROR或裸HTTP_<status>(代理/WAF 返回的 HTML、空响应)这类临时error_code绝不会把账号标记为死亡:它会先产生一次account_token_refresh_retry,然后流水线切换账号,维护循环稍后重试。 因此refresh_token_dead: false的含义是"去查网络或代理",而不是 "重新登录"。
IAM Identity Center 登录后报 profileArn is required for this request
- 原因: 账号行缺少
profile_arn。旧版直接登录可能只保存 OIDC token 与 start URL,却没有发现 Kiro profile,导致运行时请求未携带必需的 profile 绑定。 - 处置: 执行
kiro-provider accounts relogin <id|email>。当前版本会由 Provider 直接调用 KiroList-Available-Profiles,在查询用量或推理前持久化选中的 ARN,全程不需要安装 Kiro CLI。身份存在多个 profile 时传入--profile-arn <arn>;ARN 不可用或候选仍有歧义时,会在写入凭证前失败。Provider 会把 token 的 OIDC 区域与该 ARN 编码的运行区域分别保存。
某行的邮箱是占位符 builder-id@aws.amazon.com
- 查看:
accounts list的EMAIL列;login命令曾打印Warning: Kiro usage did not include an account email; storing the placeholder ...。 - 原因: IAM Identity Center / Builder ID 的设备码流程不返回邮箱;Provider 从 Kiro
getUsageLimits的响应中补齐。若该查询失败或响应不含email, 就会保存占位符。 - 处置: 用量接口可达后执行
kiro-provider accounts refresh <id>,下一次 成功的用量同步会更新邮箱。占位符不影响使用(选择器按账号 ID 工作),但对 占位符行执行accounts relogin时会接受任意 Kiro 身份,请先确认这一行 就是你想重新认证的账号。多个占位符行无法靠邮箱区分,请使用ID列。
quota-exhausted 与 overage-blocked;402 quota_exhausted 与 402 paid_overage_blocked
- 查看:
accounts list --details的AVAILABILITY、USAGE、OVERAGE列;审计事件quota_exhausted_account_persisted(warn,account_hash、recheck_after)、quota_exhausted_accounts_excluded(info,account_count)、quota_exhausted_account_recovered(info); HTTP402,error.code为quota_exhausted或paid_overage_blocked(/v1/messages上是429 rate_limit_error,错误码在 message 中)。 - 原因:
quota-exhausted:Kiro 报告该账号额度已用完(上游402,或用量快照 达到上限)。账号仍然健康,只有到期的权威用量探测确认新额度周期后才 回池(quota_recheck_interval_ms,或 Kiro 报告的重置时间)。overage-blocked:账号仍有额度或处于付费超额,但stop_on_overage(默认true)会排除超额次数超过overage_threshold(默认0)的 账号。这是选择门禁,不是健康信号;下一次用量同步会重新评估。402 quota_exhausted:所有本可用的账号都已耗尽。402 paid_overage_blocked:所有本可用的账号都只因超额门禁被排除。
- 处置: 额度耗尽时等待重置(
Retry-After头与rate_limit_wait_for_reset给出等待时长)或增加账号。超额阻塞时需要 明确决策:设置stop_on_overage: false表示有意消耗付费超额,或提高overage_threshold。不要把账号标记为不健康,它并没有问题。
503 no_healthy_accounts 或 503 upstream_token_refresh_failed
- 查看: HTTP
503,error.code为no_healthy_accounts("All accounts are unhealthy or rate-limited")或upstream_token_refresh_failed("Token refresh failed for every usable Kiro account");审计事件account_token_refresh_failed/account_token_refresh_retry、rate_limit_wait_for_reset(wait_ms、remaining_ms);以及accounts list --details中每个账号 的原因。 - 原因:
no_healthy_accounts表示在request_timeout_ms内没有账号通过 选择:全部处于rate-limited、unhealthy、needs-relogin、quota-exhausted或overage-blocked,或者剩余账号无法使用该模型 (account_model_unavailable)。若最早的限流重置时间落在请求期限内, 流水线会等待而不是直接失败。upstream_token_refresh_failed表示本次请求中 每个候选账号的 access token 刷新都失败了;同时影响所有账号的网络或代理 故障正好会产生这种结果。 - 处置: 先看可用性列。
needs-relogin→accounts relogin;rate-limited→ 等到RECHECK_AT;overage-blocked→ 见上一条。对于upstream_token_refresh_failed,检查proxy_url与出网连通性,然后执行kiro-provider accounts refresh --all确认令牌端点重新可达。GET /ready返回no_active_accounts是同一状况在就绪探针上的表现。
流式请求
502 upstream_stream_incomplete / upstream_stream_error,以及发布前重试事件
查看: 非流式请求返回 HTTP
502,error.type为upstream_error并带 错误码;流式请求在终止帧中携带同一错误码(Responses 为response.failed,Chat 为error帧,Anthropic 为overloaded_error)。 审计日志中事故本身是sdk_stream_upstream_error(warn,含error_code、error_disposition、error_type、消息哈希、raw_event_count、last_event_type、event_type_counts)或sdk_stream_idle_timeout(warn,idle_timeout_ms)。围绕它,流韧性层 会输出:事件 级别 字段 含义 sdk_stream_attempt_retrywarnattempt、max_attempts、error_code、same_account、account_hash非流式收集在形成可用结果前失败;尚未发布,允许有界替代。 sdk_stream_attempts_exhaustedwarnattempt、max_attempts、error_code、account_hash非流式收集预算已用尽,最后失败成为 HTTP 502。sdk_stream_empty_completion_retrywarnattempt、max_attempts、account_hash非流式收集得到有凭证的空完成,预算内允许同账户替代一次。 sdk_stream_transport_error_after_completionwarnerror_code、account_hash、completion_witnessed在权威完成见证(token 用量或有效的 metering 事件)之后传输层失败。已完成的轮次照常交付;错误只记录、不上抛。 原因:
upstream_stream_error是读取器、解码器、传输或内嵌上游错误;upstream_stream_incomplete是没有完成见证的干净 EOF。按契约两者都是 临时性错误。v3.1.1 起,已接纳流即使只有生命周期或部分工具参数也不重放。 应结合X-Request-ID、attempt_id、阶段和首末错误定位; 没有重试日志并不能确定哪个上游组件失败。处置: 下游以相同会话键发起替换尝试重试(见契约)。运维侧,同一个 请求上应先核对真实上游状态和 request ID,再判断账号、区域或网络原因。 原始活动只刷新空闲计时,不重置总预算;提高
stream_max_attempts不能改变已接纳流的重试边界。替代请求仍须遵守预算与副作用去重要求。
解读 sdk_stream_terminal:"助手宣布了下一步然后就停了"
每个流结束时恰好输出一条 sdk_stream_terminal(info)。字段: terminal_provenance、completion_witnessed、witness_kind、 reasoning_chars、visible_chars、tool_count、tool_intent_open、 finish_reason_synthesized,以及 account_hash 和 conversation_hash。
terminal_provenance | 发生了什么 | 责任方 |
|---|---|---|
normal_complete | Kiro 发送了完成见证,流干净地关闭。 | 无:这是 Kiro 结束本轮。 |
idle_timeout | 超过 stream_idle_timeout_ms 没有上游事件。 | 传输 / 上游停滞。 |
upstream_error | SDK 读取器或 Kiro 在流中报告错误。 | 传输 / 上游。 |
consumer_cancel | 客户端在流结束前关闭了响应。 | 客户端(自身超时或用户取消)。 |
external_abort | Provider 主动中止上游:request_timeout_ms 到期、关停或锁被破坏。 | Provider 配置或生命周期。 |
当用户反馈"模型说了接下来我会运行测试然后就停了":
- 找到该轮次的
sdk_stream_terminal。 terminal_provenance: normal_complete、completion_witnessed: true且tool_count: 0表示 Kiro 在没有发出工具调用的情况下结束了本轮。Provider 已交付收到的全部内容;这个"停止"是模型行为,不是流被截断。tool_intent_open: true记录了可见文本以一个宣布的动作结尾却没有对应的 工具调用,这正是比较提示词或投影模式时应统计的模式。finish_reason_synthesized: true在这里是正常的:Kiro 不暴露停止原因, Provider 根据tool_count推导出end_turn/tool_use。upstream_error或idle_timeout是传输事故:客户端已收到类型化流内 错误,应按契约重试;查看对应的sdk_stream_upstream_error/sdk_stream_idle_timeout以及重试事件。consumer_cancel表示客户端先离开了。先检查客户端的读取/空闲超时,再 怀疑网关;Provider 随即中止上游请求,释放账号租约。- 把
visible_chars与reasoning_chars和客户端显示的内容对照。normal_complete下reasoning_chars很大而visible_chars很小,是模型 思考很多回答很短,不是流丢失。
Reasoning 回放
400 invalid_reasoning_signature
- 查看: HTTP
400,error.code为invalid_reasoning_signature(Anthropic 信封:invalid_request_error,message 相同)。上游消息会指出 出错的块,例如messages.1.content.0: Invalid signature in thinking block。 - 原因: Kiro 在服务端校验回放的
thinking签名。客户端回放的thinking块的signature被修改、截断、重新编码,或来自其他模型/供应商。 改变被签名的 system/tools/历史前缀,即使留在同一账号也可能使签名失效。 跨账号迁移仅适用于已经验证的模型、区域、runtime 和 profile 单元,不保证任意前缀变化。 - 处置: 原样回放完整的原签名上下文。无法恢复时,保留旧 transcript,将可见 任务状态交接到新会话,并明确说明隐藏 reasoning 的损失。除迁移后的 portable 回放 尝试外,Provider 遇到该错误不会重试、不换账号、不静默降级,也不会把账号标记为 不健康。若该
400出现在刚被reasoning_replay_account_failover: "verified"迁移到其他账号的尝试上,则按下文400 reasoning_replay_migration_rejected处理: 只在原账号、原 conversation 上回退一次,原账号不可用时客户端收到的错误码为reasoning_replay_migration_rejected而不是本错误码。
400 ... is not a valid single assistant reasoning block(Claude Code)
- 查看:
/v1/messages返回 HTTP400,审计 code 为invalid_reasoning_replay,路径类似messages.11.content.1。 - 原因: 某些旧版网关响应会在同一个 Claude Code assistant 工具轮次中留下 两个不同的、仅含签名的空
thinking块。Kiro 历史只有一个 reasoning slot, 不能同时回放两个签名,猜测任意一个也不安全。 - 处置: 当前版本保留可见 assistant/tool 历史,省略该消息中全部冲突的空 direct-reasoning 信封;响应头返回
x-kiro-reasoning-replay-mode: conflict-omitted,并记录只含消息/块数量的anthropic_reasoning_replay_conflict_omittedwarn 审计。兼容范围刻意收窄:非空 reasoning、Providerkr1_/kr2_token、redacted 块或混合类型冲突仍 fail closed。
Fable 上游 reasoning 签名冲突
Fable 启用 thinking 且实际 display: omitted 时,可以在发布任何输出之前, 整组省略冲突的 signature-only 前缀。前缀限制为 128 个事件、1 MiB,不能含非空 reasoning 文本或 redacted payload。Provider 继续同一次上游请求,返回 x-kiro-reasoning-replay-mode: conflict-omitted 和只含计数的 anthropic_output_reasoning_conflict_omitted 审计,不捕获、存储或铸造该轮 reasoning token。非空、混合、晚到或超限冲突继续拒绝。HTTP 头已提交后,错误 通过 SSE error 终止流,不能再把 HTTP 状态改成 502。
Claude 升级后撤下 TaskOutput
旧 Claude 历史可能包含已完成的 TaskOutput 调用,而新客户端不再声明该工具。 Messages 现在将这些 call/result 历史独立于本轮工具声明进行校验;新输出仍必须 符合本轮 tools 和 schema。旧 Provider 的 missing_tool_declaration 不能靠反复 continue 消除。
这个本地修复不重写签名历史。如果工具前缀改变后出现 invalid_reasoning_signature, 按上面的签名上下文指引恢复;不要替不可用工具虚构本轮声明。
内存压力停止后台命令与 ConnectionRefused
先检查 Provider 监听、systemctl --user status kiro-provider.service 及其 journal, 不要先假设防火墙故障。Provider 被 OOM killer 杀死后无法接受本地连接。Claude 还可能独立回收空闲后台命令;该通知本身不能确定哪个进程耗尽了内存。
Provider 的全局请求/请求体预算会在上游 dispatch 前,以 503 和 Retry-After: 1 拒绝超额工作。它补充每账号并发限制,但不等于 heap 上限,也不能解决其他宿主 进程的内存压力。自动重启延迟时,应检查父 cgroup 和 user manager。不要默认关闭 压力保护、自动重跑被停止的重型 gate,或通过删除 replay 数据处理 OOM。
400 unsupported_reasoning_plaintext_replay(Responses)
- 查看: HTTP
400,error.code为unsupported_reasoning_plaintext_replay,param指向input[i]的 reasoning 条目;invalid_reasoning_replay是encrypted_content格式错误或不是 Providerkr1_/kr2_token时的同类错误码。 - 原因: 客户端回放的
reasoning条目只带明文summary/content,而 该轮次里任何位置都没有encrypted_content。Provider 从不把明文推理转成 提示词,因此无法投影。 - 处置: 请求时带上
include: ["reasoning.encrypted_content"],回放该 轮次时把返回的encrypted_content(默认kr2_...,兼容历史kr1_...)原样放回 reasoning 条目。 每轮恰好一个 reasoning 条目携带令牌;同一轮次的其他 reasoning 条目可以 保留明文摘要。无法保存令牌的客户端应从历史中省略 reasoning 条目,而不是 只发摘要。
400 reasoning_replay_expired
- 原因: 当前
kr2_已到认证绝对过期时间、数据库kr1_已到 idle TTL, 或预发布kr2_已超过持久化兼容截止时间。重启 Provider 不会延长这些期限。 - 处置: 从带有新 token 的后续 assistant 轮次继续,或创建不含已过期 reasoning 条目的新分支/会话。保留旧 key 不能绕过 token 内认证的有效期。
upstream_affinity_selected 中的 reasoning_replay_locked: false
- 查看:
info事件upstream_affinity_selected(每次尝试一条),含affinity_kind、affinity_bound、account_hash、conversation_hash与reasoning_replay_locked。 - 含义:
reasoning_replay_locked: true表示请求回放的加密推理仍绑定到 铸造它的账号,因此本次请求禁用账号故障切换(失败时返回reasoning_replay_*而不是换账号)。false表示没有 replay、本地不绑定的原生 Anthropicthinkingsignature,或经过认证的 Provider token 命中了精确验证的 迁移单元。真实迁移还会输出只含哈希的reasoning_replay_account_migrated。false是正常值,不是错误。
400 reasoning_replay_migration_rejected
查看: HTTP
400,error.code为reasoning_replay_migration_rejected(message 为Kiro rejected the request after its signed reasoning history was migrated to another account, and the original account is unavailable),以及 同一request_id的审计序列:reasoning_replay_account_migrated(info):已验证的 portable 回放正被派发到 其绑定单元以外的账号。每次真实迁移只发一条;之后延展已提交单元的尝试(空完成 重试、被拒绝的第二跳回退)不算迁移,不再发出。字段:request_id、protocol、model、from_account_hash、to_account_hash、replay_count、legacy_replay_count、binding。binding: "deferred"表示显式 session affinity 绑定要等 Kiro 接受本次尝试后才提交;binding: "none"表示请求没有显式 session affinity store,因此没有可提交的持久绑定。reasoning_replay_migration_committed(info):Kiro 已接受迁移请求(流式为 首个事件,非流式为完整收集完成),若存在持久绑定,此时才改为新账号和新 conversation。字段:request_id、protocol、model、from_account_hash、to_account_hash、conversation_hash、replay_count。reasoning_replay_migration_commit_failed(warn):Kiro 已接受迁移请求,但 向 accounts 数据库写入新绑定失败(磁盘满、I/O 错误)。已接受的回答仍会正常 返回,持久绑定继续指向原账号,下一请求会重新解析。字段:与committed相同, 另加error_type(错误类名)和error_code(驱动错误码,如SQLITE_FULL, 存在时才有)。reasoning_replay_migration_rejected(warn):Kiro 在产生任何输出前拒绝了 迁移请求。字段:request_id、protocol、model、from_account_hash、to_account_hash、replay_count、upstream_status(HTTP 状态码)、upstream_code(上游 reason 枚举,REQUEST_BODY_INVALID或THINKING_SIGNATURE_INVALID;签名拒绝以不带 reason 的纯消息400形式到达时 省略)、fallback、fallback_blocked_reason。fallback: "origin"表示已在 原账号和原 conversation 上回退一次;fallback: "none"表示回退被阻止,fallback_blocked_reason给出原因:fallback_spent、origin_missing、origin_ineligible、origin_quarantined、origin_unselectable或dispatch_budget。
这些事件只含哈希、计数、枚举值以及错误类名或驱动错误码;绝不包含消息文本、 提示词、签名或账号标识。
原因: 请求回放了已验证的 portable signed reasoning,但无法留在原账号 (额度耗尽、限流、不健康、模型不符、被隔离,或已达
account_inference_concurrency),于是reasoning_replay_account_failover: "verified"把它迁移到另一个账号并使用新的 Kiro conversation。Kiro 在服务端 校验迁移后的请求体,在产生输出前以400 ValidationException/REQUEST_BODY_INVALID(Improperly formed request.)或无效 reasoning 签名拒绝。 Provider 在 Kiro 接受迁移请求之前绝不重写持久绑定,因此会话仍指向原账号。 原账号可选时,Provider 只在原账号、原 conversation 上重试一次,客户端通常会在warn事件旁看到正常响应。只有这一次回退被阻止时才会返回400:原账号仍不 可用、本次请求已经回退过,或上游派发预算已耗尽。处置: 等原账号恢复(额度重置、健康恢复或
kiro-provider login重新登录) 后重发;绑定仍指向它。若原账号无法恢复,从不含 signed reasoning 的历史继续 (去掉encrypted_content/thinking块或另起新会话),并明确接受隐藏 reasoning 的损失。要完全禁用迁移,设置reasoning_replay_account_failover: "strict";此后 owner 失败会返回下文的 replay 绑定错误码而不是迁移会话。不要 自动删除 reasoning、合并历史或让客户端循环重试:Provider 已经执行了唯一安全的 回退,Kiro 具体拒绝的字段仍在调查中。
replay 绑定账号错误码
owner-bound signed reasoning 无法继续时,Provider 保留原因且不切账号: reasoning_replay_account_quota_exhausted(402)、 reasoning_replay_account_rate_limited(429)、 reasoning_replay_account_reauthentication_required(403)、 reasoning_replay_account_refresh_failed(503)、 reasoning_replay_model_unavailable(503),或 owner 不可用/不健康 503。上游 401/invalid bearer 后的强制刷新若发现凭证永久失效,返回重新登录 403;若是网络/ 临时刷新失败,返回 refresh 503,不再降级成通用 reasoning_replay_account_unavailable。前者重新登录,后者检查代理/出网并在不更换 replay token 和账号的前提下重试。
进程与配置
启动失败 service_instance_already_running;日志出现 single_instance_lock_busy 或 single_instance_lock_compromised
- 查看: 启动错误
Another kiro-provider instance already holds the service lock at <path> (gave up after N attempt(s) ...; a lock left behind by a dead process becomes stale after 15000 ms),错误码service_instance_already_running;审计事件single_instance_lock_busy(warn,首次重试:retry_attempts、retry_delay_ms、stale_ms)、single_instance_lock_acquired(info,attempts)、single_instance_lock_compromised(error,error_code、stale_ms、update_ms、handler_count)。 - 原因:
enforce_single_instance: true(默认)在平台配置根目录下持有 一个锁文件(instance_lock_path可覆盖)。锁每 5 s 刷新一次,持有者停止 刷新 15 s 后视为过期;获取时最多重试 20 × 1 s,因此SIGKILL后重启会在 过期窗口过后成功。single_instance_lock_compromised表示运行中丢失了锁 (锁目录被删除,或进程被冻结导致 mtime 刷新错过了过期窗口)。Provider 会失败关闭:停止接受请求,最多排空 10 s,然后以退出码1退出,交由 服务管理器重启。 - 处置: 对于
already_running,用systemctl --user is-active kiro-provider.service确认只定义了一个服务,且同一用户没有前台运行的serve;为另一个系统用户运行第二份会选择不同的配置根目录和锁。对于锁 被破坏,保持配置目录完整,并排查主机休眠/恢复或超过 15 s 的 I/O 停顿。 关闭锁(enforce_single_instance: false,会记录single_instance_protection_disabled)不是修复:两个进程轮换同一批 refresh token 会相互作废。
config_file_permissions_loose
- 查看: 审计事件
config_file_permissions_loose(warn),含path、mode(如0644)、recommended_mode: "0600"和hint。 - 原因: POSIX 上配置文件对组或所有人可读,而它包含
api_keys。 - 处置:
chmod 600 <path>。README 中的 systemd 单元设置了UMask=0077,服务自己创建的文件是私有的;配置文件由你创建。
启动失败 unknown key "..." (did you mean "...")
- 查看: 绑定端口之前打印的
ConfigLoadError;没有审计事件,因为进程 没有走到serve。 - 原因: 配置文件按严格模式校验。拼写错误和其他版本的键会被拒绝,并给出 最接近的建议;环境变量使用 CONFIGURATION.zh-CN.md 中列出的
KIRO_PROVIDER_*名称,报错时以(from KIRO_PROVIDER_...)标注。 - 处置: 改名或删除该键。旧的
opencode_auth_db_path仍被接受但会被 忽略,并记录config_opencode_auth_db_path_deprecated。
启动失败 auth_source "opencode-shared" was removed in kiro-provider 0.7.0
- 查看: 启动消息本身:
Copy the OpenCode accounts once with "kiro-provider accounts import [--from <path>]", then set auth_source to "local" or delete the key。 - 原因: 实时读取 OpenCode 数据库的兼容模式在 0.7.0 中移除,因为它重新 引入了跨进程凭据所有权,并可能在共享 SQLite 锁上阻塞事件循环。
- 处置: 以运行服务的同一系统用户执行一次性导入,然后把
auth_source设为"local"(或删除该键;local是默认值)。之后不再读取 OpenCode 数据库。
Claude Code 会话标题变成首条输入(例如 /model)
- 查看: 会话列表显示的是字面首条输入(
/model、某个 slash command 或开场 提示词),而不是 AI 生成的标题,transcript 中也没有ai-title记录。在早于本 版本的 Provider 上,日志会出现/v1/messages的protocol_projection_rejected,code为unsupported_parameter、param为output_config.format;随后通常还有一次unsupported_message_field、param为messages.1.output_config的拒绝,因为 Claude Code 会把该字段挪到 message 上重试一次。在当前版本上,若仍看到unsupported_structured_output且param为output_config.format的拒绝,来源通常是 prompt hook 评估请求 (ok/reason/impossibleschema),它不在标题 profile 内、按设计 fail closed,并不是标题请求失败。 - 原因: Claude Code 2.1.280 通过一个携带
output_config.format的旁路请求 生成标题(json_schema,根 object 恰好一个 required string 属性title)。旧版 Provider 只接受output_config.effort,标题请求因此失败,客户端回退为首条提示词。 - 处置: 升级 Provider。当前版本会把该形状识别为有界本地
single-string-object-v1profile,缓冲上游文本并在本地验证,然后返回一个包含{"title":"..."}的 text block,并带x-kiro-structured-output: single-string-object-v1。每次识别都会写入 info 事件anthropic_structured_output_enforced(只含 request id、模型、是否流式、profile 名称,以及 Schema 与属性名的哈希);发布失败写入 warn 事件anthropic_structured_output_failed,只含 code(structured_output_validation_failed、structured_output_buffer_exceeded、structured_output_unexpected_tool_call、structured_output_unexpected_reasoning),绝不包含文本。仍有两种情况属于客户端行为,任何 Provider 都无法修复:会话输入只有 slash command,或所有提示词都不足 10 个字符,因为这两种情况下 Claude Code 根本 不会请求标题。既有 transcript 不会被改写,可用/rename手动命名。
413:请求体过大与上下文超长
- 查看: HTTP
413。error.code为request_too_large(message 为Request body exceeds the N byte limit)是入口的请求体上限。error.type为upstream_error、error.code为context_length_exceeded的413, 则是 Kiro 以提示词超过模型上下文为由拒绝(结构化 reasonCONTENT_LENGTH_EXCEEDS_THRESHOLD/PROMPT_TOO_LONG,或旧响应里的input is too long文本);Provider 把该上游400重新映射为413,以便 客户端把它与格式错误的请求区分开。 - 原因: 前者是请求体超过
max_request_body_bytes(默认 10 MiB;更大的 请求体 Bun 会在 JSON 信封之前直接返回纯413)。后者是会话历史、工具结果 或附带文档超过了模型的上下文窗口。 - 处置: 请求体上限只在负载确实合理时(大体积内联文档)才提高
max_request_body_bytes。上下文超长必须由客户端压缩历史;Provider 不会 代替模型截断、总结或丢弃消息。request_shape诊断 (input_text_chars、document_count、tool_result_count)能显示请求的 哪一部分在膨胀。
代理问题(proxy_url)
- 查看: 启动时的
ConfigLoadErrorproxy_url must be a valid URL/proxy_url must be http(s);运行时先是account_token_refresh_retry,再是error_code: NETWORK_ERROR或裸HTTP_<status>的account_token_refresh_failed,带传输错误码(ECONNREFUSED、ECONNRESET、ETIMEDOUT、ENOTFOUND、EAI_AGAIN)的sdk_stream_upstream_error,model_catalog_refresh_failed,最终是503 upstream_token_refresh_failed或503 no_healthy_accounts。sdk_connection_pool_selected(info)显示每个账号的http_keep_alive与池命中情况,但不包含代理地址。 - 原因:
proxy_url把所有出网流量(模型调用、令牌刷新、用量探测、 设备码登录)都经由同一个 HTTP(S) 代理。不支持 SOCKS。返回 HTML 拦截页的 代理会产生HTTP_<status>刷新错误,这类错误被有意视为临时性,因此账号 停留在rate-limited而不是needs-relogin。 - 处置: 在服务用户的 shell 中验证代理(
curl -x "$PROXY" https://oidc.us-east-1.amazonaws.com/);在配置文件或KIRO_PROVIDER_PROXY_URL中设置proxy_url(serve --proxy优先级高于 两者);重启服务。kiro-provider accounts refresh --all是最快的端到端 检查,因为它通过同一套代理解析同时访问令牌端点和用量端点。
请求形态诊断(request_shape,debug)
设置 log_level: "debug"(或 KIRO_PROVIDER_LOG_LEVEL=debug)后,每个 Responses、Messages、Chat 请求都会在构造出规范请求之后、账号选择之前输出一条 request_shape 事件。它只包含计数、布尔值、一个哈希和两个标签:
| 字段 | 含义 |
|---|---|
request_id | 每个公开请求随机生成的关联 ID,与投影、SDK dispatch 和终止事件共用。 |
protocol | responses、anthropic-messages 或 chat-completions。 |
projection_mode | v3-auto、safe、native-context-safe 或 legacy-user-prefix。 |
model | 请求的公开模型名。 |
message_count | 适配后的规范消息数。 |
user_message_count、assistant_message_count、tool_message_count、instruction_message_count | 角色计数(instruction_message_count 为 system 加 developer)。 |
tool_declaration_count | 声明的工具数。 |
tool_call_count | 历史中的工具调用数(assistant 的 toolCalls 加 tool_use 内容块,同一消息内按 id 去重)。 |
tool_result_count | 历史中的工具结果数。 |
orphan_tool_result_count | 调用 id 在更早消息中找不到对应调用的结果数。 |
image_count、document_count | 内联附件数。 |
has_reasoning_replay、reasoning_replay_count | 请求是否携带加密推理回放,以及数量。 |
system_instruction_present | 存在顶层 instructions/system 或 system/developer 消息。 |
input_text_chars | 消息文本、工具结果文本与 instructions 的长度之和。只是大小,不是内容。 |
tool_set_hash | 排序后工具名的 auditHash,用于在不记录工具名的前提下关联相同工具集的请求。 |
用它来回答"客户端是否发送了完整历史?"、"是否有工具结果没有对应的调用?"、 "这段会话有多大?",而无需开启请求体日志(Provider 也不提供这种日志)。
在 debug 级别,同一个 request_id 还会出现在 request_projection_completed 与 request_history_built。每次真实 SDK 发送都会在 info 级别输出 sdk_dispatch_started 和从 1 开始的 attempt; completion witness 与 sdk_stream_terminal 会复用同一组关联字段。 request_transform_rejected 只记录阶段、错误码和来源路径。所有字段均为计数、 枚举、长度或哈希。