GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Optionally raise instead of swallowing JSON decode errors by jakobovski-arb · Pull Request #1027 · massive-com/client-python

Conversation

@jakobovski-arb

@jakobovski-arb jakobovski-arb commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

The problem

_get and _paginate_iter both decode responses like this:

try:
    obj = self._decode(resp)
except ValueError as e:
    logger.error("Error decoding json response: %s", e)
    return []

A truncated or malformed body therefore becomes an empty result, which a caller cannot distinguish from "the API had nothing to return". The only trace is a log line, and the call itself succeeds.

In _paginate_iter this is worse than an empty result: the bare return ends the generator early, so a partial page silently truncates a result set that still looks complete to the caller.

This is not hypothetical. In a single multi-hour backfill over /v3/trades, we logged 12 of these:

RESTClient ERROR: Error decoding json response: Unterminated string starting at: line 1 column 570332 (char 570331)
RESTClient ERROR: Error decoding json response: Expecting ',' delimiter: line 1 column 190527 (char 190526)
...

Each one produced an empty page that our pipeline recorded as a real absence — "this ticker had no auction print that day" — and cached permanently, because an empty response from a working API is a legitimate answer in our domain. We only found out by reading the log afterwards. For data ordered newest-first, where the row of interest sits on the last page, a truncated early page can drop exactly the record you were querying for.

The change

RESTClient(raise_on_decode_error=True) raises a new ResponseDecodeError from both decode sites instead of returning []. The flag threads to VXClient; other BaseClient subclasses inherit it through RESTClient's super().__init__().

Default is False, so existing behaviour is unchanged and this is not a breaking change.

Caveats, in the interest of full disclosure

  • We think raising should eventually be the default. Converting an undecodable body into a successful empty result is a correctness hazard for any consumer that treats "empty" as data, and there is currently no way to detect it programmatically at all. We made it opt-in only to keep the change non-breaking — if you'd prefer it default-on for a major version, or gated by an env var / module-level default instead of a constructor kwarg, we're happy to rework it.
  • The _paginate_iter half is the more important one, and it is arguably a bug rather than a design choice: silently returning a truncated result set is different in kind from returning an empty one, and no flag setting makes a partially-consumed generator obvious to the caller. If you'd rather fix only that site, or always raise there while keeping _get lenient, that would still address the dangerous case.
  • A retry would be the natural follow-up. A truncated body is usually transient, so the SDK could reasonably retry a failed decode the way urllib3 retries a failed connection, rather than pushing that on every caller. We didn't include it here to keep this diff to one concern.
  • Naming is yours to pick. raise_on_decode_error / ResponseDecodeError seemed closest to the existing BadResponse, but rename freely.
  • Test note: test_rest/models/test_requests.py::RequestTest::test_clint_headers_concat already fails on a clean master checkout in our environment (verified by stashing this change), so it is unrelated to this PR. The other 47 tests pass, including the 4 added here.

Tests

test_rest/test_decode_errors.py covers: the default still returns an empty result, the opt-in raises ResponseDecodeError, the flag reaches client.vx, and a well-formed body is unaffected either way.


Context: we're the authors of #1026 (maxsize on the connection pool). Both issues surfaced from the same workload — a high-concurrency historical backfill of /v3/trades — where silent data loss is expensive and hard to detect after the fact.

🤖 Generated with Claude Code

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@Jakobovski
fix(rest): optionally raise instead of swallowing JSON decode errors
dc2f6d9
_get and _paginate_iter decode every response inside `except ValueError:
logger.error(...); return []`, so a truncated or malformed body becomes an empty
result that a caller cannot distinguish from "the API had nothing". In
_paginate_iter it is worse: the bare return ends iteration early, so a partial
page silently truncates a result set that still looks complete.

RESTClient(raise_on_decode_error=True) raises ResponseDecodeError instead. The
default is unchanged, so this is not a breaking change.
@jakobovski-arb
jakobovski-arb requested review from jbonzo, justinpolygon, kschoche, lukeoleson, mmoghaddam385 and suever as code owners August 4, 2026 13:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@justinpolygon justinpolygon Awaiting requested review from justinpolygon justinpolygon is a code owner
@suever suever Awaiting requested review from suever suever is a code owner
@kschoche kschoche Awaiting requested review from kschoche kschoche is a code owner
@lukeoleson lukeoleson Awaiting requested review from lukeoleson lukeoleson is a code owner
@mmoghaddam385 mmoghaddam385 Awaiting requested review from mmoghaddam385 mmoghaddam385 is a code owner
@jbonzo jbonzo Awaiting requested review from jbonzo jbonzo is a code owner

At least 1 approving review is required to merge this pull request.

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

2 participants

@jakobovski-arb @Jakobovski
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.