Skip to content

Report socket failures as reasons, not as MatchError messages - #7

Merged
gmile merged 3 commits into
mainfrom
claude/socket-failure-reasons
Aug 20, 2026
Merged

Report socket failures as reasons, not as MatchError messages#7
gmile merged 3 commits into
mainfrom
claude/socket-failure-reasons

Conversation

@gmile

@gmile gmile commented Aug 20, 2026

Copy link
Copy Markdown
Owner

URP.Protocol wrapped :gen_tcp.send and :gen_tcp.recv in a match on success, so a socket that timed out or closed mid-call raised a MatchError. Every layer above rescues and stringifies, so the only account a caller got of a timed-out conversion was this:

{:error, "no match of right hand side value:\n\n    {:error, :timeout}\n"}

Two changes:

URP.Protocol now raises URP.SocketError, which carries the reason :gen_tcp reported.

URP.convert/2 and friends return that reason as an atom — :timeout, :closed, a POSIX error — while anything soffice itself said stays a message string. That is the distinction a caller actually needs: retrying a document soffice refused is pointless, and a wedged soffice is not the document's fault. The bang functions re-raise a socket reason as the URP.SocketError it was, so they still raise something with a readable message.

Breaking for callers that assumed every reason was a binary, so this wants a minor bump. VERSION and the changelog heading are left alone — release.sh minor cuts those.

Tests cover both reasons over a real socket pair, a body that stops arriving mid-frame, a send to a dead socket, the message wording, and an atom reason passing through checkout_outcome/4. mix test, mix format --check-formatted, mix dialyzer, mix xref graph --format cycles and mix docs --warnings-as-errors all pass locally; the integration suite against a real soffice has not been run here.

gmile and others added 3 commits August 20, 2026 10:18
Protocol wrapped :gen_tcp.send and :gen_tcp.recv in a match on success, so a
socket that timed out or closed mid-call left a MatchError whose message quoted
the raw {:error, :timeout} tuple. Everything above it rescues and stringifies,
so that message was the whole account of the failure a caller ever got.

URP.SocketError carries the reason :gen_tcp reported instead, which is the thing
a caller wants to act on.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A conversion could fail because soffice refused the document or because soffice
stopped answering, and both arrived as {:error, <string>}. A caller had nothing
to branch on: retrying a refused document is pointless, and a wedged soffice is
not the document's fault.

Socket failures now come back as the atom :gen_tcp reported, so :timeout,
:closed or a POSIX error, and everything soffice itself said stays a message
string. The bang functions re-raise a socket reason as the URP.SocketError it
was, so they still raise something with a readable message.

Callers that assumed every reason was a binary have to handle both shapes, hence
the minor version.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gmile
gmile merged commit ece8949 into main Aug 20, 2026
1 check passed
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