Skip to content

sdk-drift: RateLimitExceeded.retryAfter is typed number (float) in TypeScript but int in Python #803

@realfishsam

Description

@realfishsam

Drift

The retryAfter / retry_after field on RateLimitExceeded is declared as number in TypeScript (which is IEEE 754 double — effectively a float) and as int in Python. The sidecar returns this value from the server error payload, so the declared type should match the wire format in both SDKs.

TypeScript SDK

sdks/typescript/pmxt/errors.ts, lines 76–82:

export class RateLimitExceeded extends PmxtError {
    public readonly retryAfter?: number;          // number = float in JS

    constructor(message: string, retryAfter?: number, exchange?: string) {
        super(message, "RATE_LIMIT_EXCEEDED", true, exchange);
        this.retryAfter = retryAfter;
    }
}

retryAfter is number | undefined. JavaScript number is a 64-bit float.

Python SDK

sdks/python/pmxt/errors.py, lines 67–72:

class RateLimitExceeded(PmxtError):
    def __init__(self, message: str, retry_after: int | None = None, **kwargs):
        super().__init__(message, **kwargs)
        self.retry_after = retry_after            # annotated as int

retry_after is int | None. The from_server_error factory at line 138 reads error_data.get("retryAfter") and passes it directly to the constructor without converting to int.

Expected

Both SDKs should use the same type. Retry-after delays from HTTP servers are typically whole seconds, so int (Python) / number (TypeScript, effectively integer-valued) is fine, but the Python annotation should either stay int with an explicit int() cast in from_server_error, or widen to float to match TypeScript semantics. Whichever is chosen should be applied consistently. Currently a fractional retryAfter from the server would be accepted by TypeScript but silently lose precision in Python.

Impact

If the server ever returns a fractional retry-after value (e.g. 0.5 for 500 ms), TypeScript will preserve it while Python will pass through a float under an int annotation — a type-checker violation and a potential runtime surprise for callers that rely on the declared type.


Found by automated SDK cross-language drift audit

Metadata

Metadata

Assignees

No one assigned

    Labels

    sdk-driftCross-language SDK consistency findings (TypeScript vs Python)

    Type

    No type
    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