Skip to content

[Detail Bug] SDK: CommandRecord.trim raises raw OverflowError instead of S2ClientError for invalid sequence numbers #8

Description

@detail-app

Detail Bug Report

https://app.detail.dev/org_89d327b3-b883-4365-b6a3-46b6701342a9/bugs/bug_0b262fcd-2183-4a66-a044-915955cd8fcf

Introduced in #1 by @quettabit on Apr 7, 2026

Summary

  • Context: CommandRecord.trim() creates a trim command record to remove records from the beginning of a stream.
  • Bug: The method does not validate that desired_first_seq_num is a valid unsigned 64-bit integer, resulting in an OverflowError instead of S2ClientError for invalid inputs.
  • Actual vs. expected: Passing an invalid value raises raw OverflowError instead of S2ClientError with a clear message.
  • Impact: Inconsistent error handling violates the SDK's own validation patterns, making debugging harder for users.

Code with Bug

@staticmethod
def trim(desired_first_seq_num: int) -> Record:
    """Create a trim command record.

    Has no effect if the sequence number is smaller than the first existing record.
    """
    return Record(
        body=desired_first_seq_num.to_bytes(8, "big"),  # <-- BUG 🔴 OverflowError escapes instead of S2ClientError
        headers=[(bytes(), CommandRecord.TRIM)],
    )

Explanation

trim() encodes desired_first_seq_num into an 8-byte unsigned big-endian integer in the client SDK. For invalid inputs (negative values or values >= 2**64), int.to_bytes(8, "big") raises a built-in OverflowError, which leaks out of the SDK. The SDK otherwise consistently validates user inputs and raises S2ClientError with actionable messages, so trim() should validate the uint64 constraint before encoding.

Codebase Inconsistency

CommandRecord.fence() validates its protocol constraint and raises S2ClientError before returning a Record, while trim() does not:

@staticmethod
def fence(token: str) -> Record:
    """Create a fence command record.

    The fencing token must not exceed 36 bytes when UTF-8 encoded.
    """
    encoded_token = token.encode()
    if len(encoded_token) > 36:
        raise S2ClientError("UTF-8 byte count of fencing token exceeds 36 bytes")
    return Record(body=encoded_token, headers=[(bytes(), CommandRecord.FENCE)])

Recommended Fix

@staticmethod
def trim(desired_first_seq_num: int) -> Record:
    """Create a trim command record.

    Has no effect if the sequence number is smaller than the first existing record.
    """
    if not (0 <= desired_first_seq_num < 2**64):
        raise S2ClientError(
            f"desired_first_seq_num must be a valid unsigned 64-bit integer, got {desired_first_seq_num}"
        )
    return Record(
        body=desired_first_seq_num.to_bytes(8, "big"),
        headers=[(bytes(), CommandRecord.TRIM)],
    )

History

This bug was introduced in commit 3dc9795.

Metadata

Metadata

Assignees

Labels

detail-bugbug flagged by https://detail.dev/

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions