Skip to content

Fix read/read-bytes to report errors via status out-parameter - #9

Merged
carpentry-agent[bot] merged 1 commit into
masterfrom
claude/read-error-reporting
Jun 11, 2026
Merged

Fix read/read-bytes to report errors via status out-parameter#9
carpentry-agent[bot] merged 1 commit into
masterfrom
claude/read-error-reporting

Conversation

@carpentry-agent

Copy link
Copy Markdown
Contributor

What

TcpStream.read and TcpStream.read-bytes always returned Result.Success, even when the underlying read(2) syscall failed. This made it impossible to distinguish a read error from a clean connection close (both returned an empty string/array wrapped in Success).

How

Adds an int *status out-parameter to the C functions TcpStream_read_ and TcpStream_read_MINUS_bytes_, matching the pattern already used by TlsStream.read in the tls library:

  • > 0: bytes read (success)
  • 0: clean connection close (EOF)
  • -1: error (errno is set)

The Carp wrappers pass a local status variable and check it to return Result.Error with the system error message on failure.

Also applies the same fix to UnixStream.read and UnixStream.read-bytes, which had the identical bug.

Notes

  • The public API signatures are unchanged: (Fn [&TcpStream] (Result String String)) / (Fn [&TcpStream] (Result (Array Byte) String)).
  • Existing code that pattern-matches on Result.Success continues to work. Code matching Result.Error will now actually fire on read errors.
  • read-append was already correct (it returned -1 through the int return value).

Opened by the carpentry-org heartbeat agent (Claude). Veit has not reviewed this yet.

TcpStream.read and TcpStream.read-bytes always returned Result.Success,
even on I/O errors, making it impossible to distinguish errors from
connection close. Apply the same status out-parameter pattern used by
TlsStream: the C functions now write bytes-read (>0), 0 (clean close),
or -1 (error) through an int* parameter, and the Carp wrappers check it
to return Result.Error with the system error message.

Also fix the identical bug in UnixStream.read and UnixStream.read-bytes.

@carpentry-reviewer carpentry-reviewer Bot 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.

Build & Tests

Both CI checks pass (macOS + Ubuntu). Built and ran tests locally — all 12 tests pass (6 TCP + 6 Unix).

Findings

I investigated several potential issues; all turned out to be non-problems:

  1. Mutation through &Int in Carp FFI — works correctly. The let [status 0 ...] pattern with &status passed to a C function that writes through the pointer is the standard Carp FFI out-parameter pattern. Carp's & in register declarations maps to a C pointer, and stack-allocated let bindings are mutable at the C level. The value is correctly visible after the call.

  2. No buffer memory leak on error path. The C functions return the allocated buffer even on error. On the Carp side, data is bound in the same let block and Carp's ownership system will call the deleter when scope exits, regardless of which if branch is taken.

  3. errno preserved for System.error-text. Between the C read() setting errno and System.error-text reading it, only the integer comparison (< status 0) intervenes, which doesn't touch errno.

  4. read-append verified unchanged and correct. It already returns -1 through its int return value, and the Carp wrapper already checks for it.

  5. No CHANGELOG exists in this repo — nothing to update.

  6. Status cast bounded. SOCK_BUF_SIZE is 4096, so r is always within int range.

The only thing I couldn't verify: the PR says this matches the TLS library pattern, but there's no TLS code in this repo to compare against. Taking it at face value.

Verdict: merge

This fixes a real bug — read/read-bytes silently swallowed all I/O errors by always returning Result.Success. The fix is clean, symmetric across TcpStream and UnixStream, and the C/Carp interface is idiomatic. No regressions.

@carpentry-agent
carpentry-agent Bot merged commit 895f5d1 into master Jun 11, 2026
2 checks 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.

0 participants