emrg: LLM client tolerates gzip response bodies without Content-Encoding - #541
Merged
Conversation
Some API gateways/proxies return gzip-compressed response bodies without a proper Content-Encoding header, so httpx does not decompress them and resp.json() crashes with UnicodeDecodeError on the gzip magic bytes (0x1f 0x8b). Observed in production at 2026-08-07 12:40: memory reflection failed with UnicodeDecodeError at llm.py:134 — the reflection died outright with no retry. Fix: - _parse_json_body(): detect gzip magic prefix and decompress transparently - chat(): catch JSONDecodeError/UnicodeDecodeError/OSError/EOFError on the 200 body and retry with exponential backoff instead of crashing - +6 tests: plain/gzip/corrupt-gzip parsing, transparent decompress, retry-then-succeed, retry exhaustion - doc counts synced 502 -> 508 (README/README.cn/Agent.md)
argszero
commented
Aug 7, 2026
argszero
left a comment
Owner
Author
There was a problem hiding this comment.
✅ LGTM — cycle
Reviewed the full diff on branch fix/llm-gzip-body-tolerance (954349a):
- _parse_json_body() — correct gzip magic-byte detection (0x1f 0x8b) with transparent decompress; pure function, no side effects.
- chat() — unparseable 200 bodies now retry with the same exponential-backoff policy as transient HTTP errors instead of crashing; clean RuntimeError after MAX_RETRIES. Exception set (JSONDecodeError/UnicodeDecodeError/OSError/EOFError) covers corrupt and truncated gzip.
- Scope is right: the production crash was in chat() (memory reflection). chat_stream() already degrades gracefully on garbage bodies (SSE parse-skip → no finish_reason → existing retry path), so no change needed there.
- +6 tests cover plain/gzip/corrupt parsing, transparent decompress, retry-then-succeed, and retry exhaustion — all pass locally (24 passed incl. doc-count guard).
- Doc counts synced 502 → 508 in README/README.cn/Agent.md.
CI green (Test run 31148570523). Fixes a real production crash observed at 2026-08-07 12:40 (UnicodeDecodeError on gzip body without Content-Encoding).
argszero
commented
Aug 7, 2026
argszero
left a comment
Owner
Author
There was a problem hiding this comment.
✅ LGTM — cycle (2nd independent review)
Re-verified on commit 954349a (no changes since last review):
_parse_json_body(): gzip magic detection + transparent decompress is minimal and correct; plain JSON path unchanged (no behavior change for well-formed responses).chat()retry path: mirrors the existing transient-error backoff policy;last_erroris set beforecontinueso exhaustion raises correctly; exception set covers corrupt (OSError/BadGzipFile) and truncated (EOFError) gzip.- Tests re-run locally: 22 passed (6 new: plain/gzip/corrupt parsing, transparent decompress, retry-then-succeed, retry exhaustion).
- CI green (31148570523).
One more LGTM needed before merge.
argszero
commented
Aug 7, 2026
argszero
left a comment
Owner
Author
There was a problem hiding this comment.
✅ LGTM — cycle (3rd independent review)
Final verification on commit 954349a (unchanged since reviews 1-2):
- Full regression suite on the PR branch: 508 passed
- Import check +
emrg --help: OK - CI green (31148570523)
3 consecutive LGTMs from different cycles, no ❌ in between — merge condition satisfied.
argszero
added a commit
that referenced
this pull request
Aug 7, 2026
…ntry (#542) Co-authored-by: EMRG Evolution <emrg@argszero.dev>
This was referenced Aug 7, 2026
argszero
added a commit
that referenced
this pull request
Aug 7, 2026
…p tolerance (#552) Version bump 0.2.10 → 0.2.11 across all 6 version sources (pyproject.toml / emrg/__init__.py / gui/package.json / uv.lock / make-installer.sh / build-runtime.sh). Release for Windows verification: - #541 LLM gzip body tolerance - #543 GUI message display fixes (#544 quick-ref) - #545 Windows GCM silent-fail Stage 1 - #546 Windows TUI CJK input + legacy arrow keys + /rant visibility - #548/#549/#550 GitHub auth in GUI (PAT + device flow + banner, Stage 2) - #551 quick-ref All 548 tests green. Co-authored-by: EMRG Evolution <emrg@argszero.dev>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Problem
Some API gateways/proxies return gzip-compressed response bodies without a proper
Content-Encodingheader, so httpx does not decompress them andresp.json()crashes withUnicodeDecodeErroron the gzip magic bytes (0x1f 0x8b).Observed in production (2026-08-07 12:40, emrgd.log):
The reflection died outright — no retry, no recovery.
Fix
_parse_json_body()— new helper: detects the gzip magic prefix (0x1f 0x8b) and decompresses transparently beforejson.loads.chat()— catchesJSONDecodeError/UnicodeDecodeError/OSError/EOFErroron the 200 body and retries with exponential backoff (same policy as transient HTTP errors) instead of crashing. AfterMAX_RETRIESit raises a cleanRuntimeError.Verification
uv run pytest tests/— 508 passedfrom emrg.client.app import run_client— OKpython -m emrg --help— OKTriggered by a real daemon crash, not speculation.