Skip to content

fix([cos-runner-client-non-json-response-handling]): tolerate non-JSON runner responses#610

Merged
atomantic merged 3 commits into
mainfrom
claim/cos-runner-client-non-json-response-handling
Jun 1, 2026
Merged

fix([cos-runner-client-non-json-response-handling]): tolerate non-JSON runner responses#610
atomantic merged 3 commits into
mainfrom
claim/cos-runner-client-non-json-response-handling

Conversation

@atomantic
Copy link
Copy Markdown
Owner

Summary

The CoS runner client (server/services/cosRunnerClient.js) called response.json() unconditionally on every runner response — both success and error paths. When the runner returns an HTML body instead of JSON (e.g. a 500 error page while PM2 is restarting it mid-request), the parse threw Unexpected token < and masked the runner's actual error.

All ~20 parse sites now route through a small readRunnerJson(response) helper that reads the raw text and parses it tolerantly via the existing safeJSONParse (server/lib/fileUtils.js), falling back to { error: <raw text> } on a non-JSON body so callers surface the runner's real message. An empty body returns {} (an empty success, distinct from a parse failure) so spreading callers like getRunnerHealth don't pick up a spurious error key.

A /simplify reuse pass replaced an initial hand-rolled try/catch with the catalogued safeJSONParse. The broader cleanup — promoting a shared tolerant fetch→JSON helper and adopting it across the other unguarded fetch clients (aiProvider.js, browserService.js, etc.) — is captured in PLAN.md as [shared-tolerant-fetch-json-helper], out of this single-file PR's scope.

Test plan

  • cd server && npm test -- cosRunnerClient — 39 tests pass (36 existing + 3 new).
  • New tests cover: a non-JSON error body surfacing the runner's text (502 Bad Gateway), an empty error body falling back to the default message, and a non-JSON success body not crashing.
  • The shared mockResponse helper was updated to serve the body via text() (objects serialized, strings passed raw) to match the new read path.

atomantic added 3 commits June 1, 2026 13:55
…N runner responses

The cosRunnerClient fetch wrappers called response.json() unconditionally on
every response. When the runner returns an HTML error page instead of JSON
(e.g. a 500 while PM2 is restarting it mid-request), the parse threw
"Unexpected token <" and masked the runner's actual error.

Route all body parsing through a tolerant readRunnerJson() helper that reads
the raw text and falls back to { error: <raw text> } on a parse failure, so
callers surface the runner's real message.
…SONParse for tolerant runner parsing

/simplify reuse pass: readRunnerJson re-implemented the tolerant-parse pattern
that server/lib/fileUtils.js#safeJSONParse already provides. Delegate to it,
keeping the empty-body -> {} guard so spreading callers don't pick up a
spurious error key. Capture the broader 'adopt a shared tolerant fetch->JSON
helper across the other fetch clients' cleanup in PLAN.md (out of this PR's
single-file scope).
@atomantic atomantic merged commit 5a770c7 into main Jun 1, 2026
2 checks passed
@atomantic atomantic deleted the claim/cos-runner-client-non-json-response-handling branch June 1, 2026 21:11
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.

1 participant