Skip to content

fix(EXT-4): handle GraphQL error and partial-data responses explicitly - #160

Merged
bougyman merged 5 commits into
mainfrom
EXT-4-graphql-error-handling
Aug 20, 2026
Merged

fix(EXT-4): handle GraphQL error and partial-data responses explicitly#160
bougyman merged 5 commits into
mainfrom
EXT-4-graphql-error-handling

Conversation

@bougyman

Copy link
Copy Markdown
Member

Summary

  • Partial-success responses (HTTP 200 with both "data" and "errors"): Api.call/2 already returned {:ok, data} for these; now also emits Logger.warning so field-level errors aren't silently discarded.
  • ManualRead else clauses: Added else clauses to every with chain in ManualRead modules (team, user, label, workflow_state, project, paginate). Without them, unexpected API shapes returned as raw values that Ash couldn't handle.
  • http_error normalization: {:http_error, status, body} (3-tuple) is normalized to {:http_error, status} (2-tuple) at the ManualRead boundary. Splode only stores 2-tuples in UnknownError.value; 3-tuples fell into a string representation that blocked pattern matching.
  • Paginate crash: Replaced Map.fetch! with a safe fetch_connection/2 helper that returns {:error, {:unexpected_response, ...}} instead of raising KeyError.
  • CLI error handlers: Added four new handle_error/3 clauses for http_error 401/403 (exit 77), http_error other (exit 88), transport_error (exit 69), and unexpected_response (exit 88). All previously fell through to the generic catch-all with no actionable message.

Test plan

  • mix test test/linear_cli/cli_test.exs — all 27 pass including new HTTP/transport/unexpected tests
  • mix test — 293/307 pass; 14 failures are pre-existing GPG signing env failures unrelated to this change
  • mix compile --warnings-as-errors — clean
  • New tests in user_test.exs (viewer=nil, absent viewer key) and issue_test.exs (absent connection field in Paginate)

Closes EXT-4

🤖 Generated with Claude Code

bougyman and others added 4 commits August 20, 2026 10:19
When the Linear API returns HTTP 200 with both "data" and "errors"
(partial-success), the field-level errors were silently discarded.
Now emits a Logger.warning so operators can see when data is
incomplete.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…uses

- Add `else` clauses to `with` chains in all ManualRead read/3 modules
  so unexpected shapes return `{:error, {:unexpected_response, ...}}`
  instead of passing the raw value through (which Ash can't handle).
- Normalize `{:http_error, status, body}` 3-tuples to `{:http_error, status}`
  2-tuples at the ManualRead boundary. Splode (Ash's error library) only
  stores 2-tuples (keyword list elements) in `UnknownError.value`; 3-tuples
  fall through to a string representation that prevents pattern matching in
  handle_error/3.
- Fix `Map.fetch!` crash in Paginate.do_all when the expected connection
  field is absent: replace with safe `fetch_connection/2` helper that
  returns `{:error, {:unexpected_response, ...}}` instead of raising.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ures

- HTTP 401/403: print targeted auth-failure message and exit 77
- Other HTTP errors: print the status code and exit 88
- Transport errors (DNS failure, ECONNREFUSED, timeout): print network
  message and exit 69
- Unexpected response shape (200 with neither data nor errors): exit 88

Previously all these fell through to the catch-all "What the heck is
this?" handler, giving users no actionable information.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add retry: false to test config to prevent Req's exponential backoff
  from slowing transport-error tests.
- Add tests for unexpected_response, http 401/403, http 500, and
  transport error paths in CLITest.
- Add UserTest coverage for viewer=nil and absent-viewer-key responses.
- Add IssueTest coverage for absent connection field in Paginate.all.
- Fix "unexpected raise" test: nil["key"] returns nil in Elixir (Atom
  implements Access), so use a non-enumerable nodes value instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 20, 2026 14:20

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Credo --strict flags a with expression that has exactly one <- clause
and an else branch as less readable than an equivalent case. Convert
all eight affected ManualRead call sites in label, project, team, user,
and workflow_state. Also reformat api.ex's partial-success clause to
satisfy mix format --check-formatted.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@bougyman
bougyman merged commit 31359ef into main Aug 20, 2026
2 checks passed
@bougyman
bougyman deleted the EXT-4-graphql-error-handling branch August 20, 2026 14:57
bougyman pushed a commit that referenced this pull request Aug 20, 2026
🤖 I have created a release *beep* *boop*
---


##
[1.16.0](v1.15.2...v1.16.0)
(2026-08-20)


### Features

* **EXT-5:** show workflow status in compact and full issue listings
([#162](#162))
([acf35e5](acf35e5))


### Bug Fixes

* **EXT-4:** handle GraphQL error and partial-data responses explicitly
([#160](#160))
([31359ef](31359ef))
* **EXT-7:** bundle musl NIF for Linux Burrito releases and container
([#163](#163))
([3172d4e](3172d4e))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
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.

2 participants