Skip to content

GH-2409: fix(executor): OpenCode response parsing decodes wrong fields — silent empty Output#2412

Merged
alekspetrov merged 1 commit intomainfrom
pilot/GH-2409
Apr 26, 2026
Merged

GH-2409: fix(executor): OpenCode response parsing decodes wrong fields — silent empty Output#2412
alekspetrov merged 1 commit intomainfrom
pilot/GH-2409

Conversation

@alekspetrov
Copy link
Copy Markdown
Collaborator

Summary

Automated PR created by Pilot for task GH-2409.

Closes #2409

Changes

GitHub Issue #2409: fix(executor): OpenCode response parsing decodes wrong fields — silent empty Output

Summary

After PR #2408 lands, the schema rejection at first send is gone, but OpenCodeBackend.sendMessage then mis-parses the success response. Pilot will see Success: true with empty Output, leading to no commit / empty PR / silent failure downstream.

Evidence (verified against OpenCode v1.4.6 source)

packages/opencode/src/server/instance/session.ts:854-895:

.post("/:sessionID/message",
  describeRoute({
    responses: {
      200: { content: { "application/json": { schema: resolver(
        z.object({ info: MessageV2.Assistant, parts: MessageV2.Part.array() })
      ) } } },
    },
  }),
  ...
  async (c) => {
    c.status(200)
    c.header("Content-Type", "application/json")
    return stream(c, async (stream) => {
      const msg = await ...svc.prompt(...)
      stream.write(JSON.stringify(msg))
    })
  },
)

So the success response is:

  • Content-Type: application/json (not text/event-stream — the Accept: text/event-stream header sent by Pilot is ignored)
  • Body shape: {info: MessageV2.Assistant, parts: MessageV2.Part[]}

Pilot's current parse path (internal/executor/backend_opencode.go:243-256):

} else {
    var msgResult struct {
        Success bool   `json:"success"`
        Output  string `json:"output"`
        Error   string `json:"error"`
    }
    json.NewDecoder(resp.Body).Decode(&msgResult)
    result.Success = msgResult.Success
    result.Output  = msgResult.Output
    result.Error   = msgResult.Error
}
if result.Error == "" { result.Success = true }

None of success/output/error exist on OpenCode v1.4.6's response. All three decode to zero values → result.Output = \"\" → final Success = true (because Error is empty).

Impact

Fix (sketch)

Map the actual response shape:

type ocAssistantMessage struct {
    Info  ocAssistantInfo `json:\"info\"`
    Parts []ocPart        `json:\"parts\"`
}
type ocPart struct {
    Type string `json:\"type\"`
    Text string `json:\"text,omitempty\"`
    // tool, file, agent, subtask variants
}

Then concatenate text parts into result.Output, surface tool-use parts as BackendEvents via opts.EventHandler (mimicking the streaming path), and pull token usage from info.

Alternative: switch to POST /session/:sessionID/prompt_async (returns 204 immediately) and consume the SSE event stream from /event for per-token deltas. That endpoint actually streams; the current synchronous /message endpoint never does despite the misleading description: \"...streaming the AI response\".

Repro (post #2408)

  1. Configure Pilot with OpenCode 1.4.x backend.
  2. Pick up a labeled GitHub issue.
  3. Wait through OpenCode execution (will run for minutes).
  4. Observe: HTTP 200 returned, but Pilot reports success with no commits / no PR content.

References

…2409)

OpenCode's POST /session/:id/message returns application/json with
{info: AssistantMessage, parts: Part[]}, not {success,output,error}.
The previous decoder pulled three nonexistent fields, then defaulted
Success=true on empty Error — surfacing a successful run with empty
Output and breaking downstream commit / PR creation silently.

Map the real shape: concatenate text parts into Output, surface
tool/step parts as BackendEvents through opts.EventHandler, and pull
token usage / model / sessionID from info. Synthesize a final
EventTypeResult so consumers see a terminal event matching the SSE
path.
@codecov-commenter
Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 76.92308% with 15 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/executor/backend_opencode.go 76.92% 11 Missing and 4 partials ⚠️

📢 Thoughts on this report? Let us know!

@alekspetrov alekspetrov merged commit 01032d0 into main Apr 26, 2026
4 checks passed
@alekspetrov alekspetrov deleted the pilot/GH-2409 branch April 26, 2026 15:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(executor): OpenCode response parsing decodes wrong fields — silent empty Output

2 participants