From 6ca8a3f4cc1d56c18e5b257d9a3674fea9d38548 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Mon, 20 Jul 2026 16:14:45 +0100 Subject: [PATCH] fix(github): log OpenCode request failure causes Restore detailed diagnostics for GitHub bot OpenCode SDK failures by logging nested error cause metadata before wrapping session list, create, and command failures. This keeps the migrated GitHub automation behavior aligned with the original branch and improves production debugging for undici, fetch, and local OpenCode loopback failures without changing command execution behavior. Validation: npm run typecheck --prefix packages/server; npm test --prefix packages/server -- "src/integrations/github/__tests__/*.test.ts" --- .../src/integrations/github/job-runner.ts | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/packages/server/src/integrations/github/job-runner.ts b/packages/server/src/integrations/github/job-runner.ts index 4e441ef9..3705d501 100644 --- a/packages/server/src/integrations/github/job-runner.ts +++ b/packages/server/src/integrations/github/job-runner.ts @@ -503,7 +503,7 @@ export class GitHubJobRunner { let sessionId: string | null = null let reusedSession = false try { - const listed = await unwrap(client.session.list({ directory, search: threadTitle, limit: 20 }, { signal: params.signal }), "session.list") + const listed = await unwrap(client.session.list({ directory, search: threadTitle, limit: 20 }, { signal: params.signal }), "session.list", this.deps.logger) const items = Array.isArray((listed as any)?.data) ? (listed as any).data : Array.isArray(listed) ? listed : [] const matches = items.filter((s: any) => s?.title === threadTitle && typeof s?.id === "string") matches.sort((a: any, b: any) => Number(b?.time?.updated ?? b?.time?.created ?? 0) - Number(a?.time?.updated ?? a?.time?.created ?? 0)) @@ -516,7 +516,7 @@ export class GitHubJobRunner { } if (!sessionId) { - const created = await unwrap(client.session.create({ directory, title: threadTitle }, { signal: params.signal }), "session.create") + const created = await unwrap(client.session.create({ directory, title: threadTitle }, { signal: params.signal }), "session.create", this.deps.logger) if (!created?.id || typeof created.id !== "string") throw new Error("OpenCode session.create returned no id") sessionId = created.id } @@ -536,7 +536,7 @@ export class GitHubJobRunner { ...(params.agent ? { agent: params.agent } : {}), ...(params.model ? { model: `${params.model.providerId}/${params.model.modelId}` } : {}), ...(params.variant ? { variant: params.variant } : {}), - }, { signal: params.signal }), "session.command") + }, { signal: params.signal }), "session.command", this.deps.logger) const infoError = response?.info?.error if (infoError) { @@ -604,11 +604,32 @@ export class GitHubJobRunner { } } -async function unwrap(promise: Promise | undefined>, label: string): Promise { +async function unwrap(promise: Promise | undefined>, label: string, logger?: Logger): Promise { const result = await promise if (!result) throw new Error(`${label} returned no result`) if ((result as any).error) { const err = (result as any).error + try { + const errName = err && typeof err === "object" ? (err as any).name : undefined + const cause = err && typeof err === "object" ? (err as any).cause : undefined + if (cause) { + const causeName = cause && typeof cause === "object" ? (cause as any).name : undefined + const causeCode = cause && typeof cause === "object" ? (cause as any).code : undefined + const causeMessage = cause instanceof Error ? cause.message : String(cause) + logger?.error( + { + label, + errorName: errName, + causeName, + causeCode, + causeMessage, + }, + "OpenCode request failed (error cause)", + ) + } + } catch { + // best-effort diagnostics only + } const msg = err instanceof Error ? err.message : JSON.stringify(err) throw new Error(`${label} failed: ${msg}`) }