Skip to content

feat: add isNotFound, isConflict and isMalformedId error predicates - #83

Merged
lmjabreu merged 3 commits into
mainfrom
lmjabreu/comms-error-predicates
Oct 1, 2026
Merged

lmjabreu merged 3 commits into
mainfrom
lmjabreu/comms-error-predicates

Conversation

@lmjabreu

@lmjabreu lmjabreu commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Why

Consumers keep re-implementing the same checks on top of CommsRequestError. comms-cli has its own copies, added in comms-cli#66, and the MCP needs the same ones for checking ids. @scottlovegrove raised it on that PR: they belong here, next to the error they inspect.

Change

Five exports in types/errors.ts, beside CommsRequestError:

  • isNotFound(error) and isConflict(error), 404 and 409.
  • getCommsErrorCode(error) and getCommsErrorString(error), the error_code and error_string from the response body, or null. Branch on the code, display the string.
  • isMalformedId(error), a 409 carrying error_code 217, which is a bad reference rather than a conflict.

isMalformedId keys on the code rather than the message, because the API pairs 217 with two different strings: one for an id that does not base58-decode to 16 bytes, and one for a version nibble that is not 7. comms-cli originally matched on the prose and missed the second.

They narrow with instanceof CommsRequestError, matching migration.ts and fetch-with-retry.ts rather than duck-typing the shape.

Not included: comms-cli's 401 and 403 helpers. isForbidden is defined there as "a 403 that is not an OAuth-scope failure", which is CLI policy rather than a property of the response, so it stays there for now.

Checks

  • npm run check, npm run type-check, npm test: passed (246 tests, 19 new).
  • npm run build, npm run attw: passed. All five are exported from the package root.
  • Mutation-tested, six mutations, 1-line diff each, each failing at least one test: isMalformedId ignoring the code, getCommsErrorCode coercing a string, getCommsErrorString coercing a non-string, getCommsErrorString falling back to error.message, and instanceof relaxed to duck-typing in both readers.

Follow-up: a comms-cli PR deleting its copies once this is released. comms-cli#67 does the same for the id validator.

🤖 Generated with Claude Code

lmjabreu and others added 2 commits September 24, 2026 16:04
Consumers currently re-implement these on top of CommsRequestError. The
comms-cli has its own copies, and the MCP needs the same checks, so they
belong next to the error they inspect.

isMalformedId keys on error_code 217 rather than the message, because the
API pairs that code with two different strings: one for an id that does
not base58-decode to 16 bytes, and one for a version nibble that is not 7.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The two read the same error body and a consumer surfacing a Comms failure
wants both: the code to branch on, the string to show. Shipping only the
code left the pair half-done.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@lmjabreu

Copy link
Copy Markdown
Contributor Author

@doistbot /review

@doistbot doistbot left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Adds five focused exports beside CommsRequestError — isNotFound, isConflict, getCommsErrorCode, getCommsErrorString, and isMalformedId — giving consumers a single home for response-error checks instead of per-repo copies, with isMalformedId sensibly keyed on error code 217 rather than prose. The helpers narrow via instanceof, guard the response-body shapes, are re-exported through the package root, and come with solid edge-case coverage plus mutation testing. No inline issues were flagged; the security and reuse passes found no concerns, noting the functions are pure read-only inspectors with no duplicated existing utility.

I also included a few optional follow-up notes in the details below.

Optional follow-up notes (2)
  • P3 src/types/errors.ts:62: The TSDoc example calls api.threads.getThread({ id }), but ThreadsClient.getThread is declared as getThread(id: string). Passing an object will not type-check. Change the example to api.threads.getThread(id).
  • P3 src/types/errors.test.ts:84: This assertion duplicates line 71 exactly: both call getCommsErrorString(requestError(409)), and requestError always supplies the same 'request failed' message. The block therefore adds no regression signal beyond the earlier "no error_string" case. Either delete this it block or make the intended scenario distinct, e.g. construct new CommsRequestError('server text', 409) so the test explicitly shows the message is not used as a fallback.

Share Feedback • Review Logs

The TSDoc example passed an object to getThread, which takes a bare id.
The fallback test repeated the no-error_string case verbatim; it now
constructs an error with its own message, so it actually shows the
message is not used as a fallback.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@lmjabreu

Copy link
Copy Markdown
Contributor Author

The TSDoc example calls api.threads.getThread({ id }), but ThreadsClient.getThread is declared as getThread(id: string).

Fixed. It's getThread(id: string) at threads-client.ts:106, so the example wouldn't have compiled.

This assertion duplicates line 71 exactly

Fixed. Now builds its own new CommsRequestError('server text', 409), so the message it must not return is visible in the test.

@lmjabreu
lmjabreu merged commit f4d2213 into main Oct 1, 2026
5 checks passed
@lmjabreu
lmjabreu deleted the lmjabreu/comms-error-predicates branch October 1, 2026 14:53
doist-release-bot Bot added a commit that referenced this pull request Oct 1, 2026
## [3.3.0](v3.2.0...v3.3.0) (2026-10-01)

### Features

* add isNotFound, isConflict and isMalformedId error predicates ([#83](#83)) ([f4d2213](f4d2213))
@doist-release-bot

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 3.3.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants