fix(multi_format_api): explode nested JSON record arrays into rows#90
Merged
gabrielbressan-tfy merged 1 commit intoJul 9, 2026
Merged
Conversation
An API response shaped as an object with a nested record array (e.g.
{"tickers": [...13k...], "status": "OK", "count": 12962}) collapsed into a
single row with the array dumped into one ~10MB string cell. That blob
propagated to downstream code-execution and blew the context budget
(4,656,292 tokens > 1,000,000). The crash is caused by shape, not raw size:
the same data as a proper 12,962-row table costs ~8.5K tokens.
parse_json only recognized a record array under a hardcoded whitelist
(data/results/items/records/rows/entries); domain keys like `tickers` missed
it and fell through to a single-row json_normalize.
Changes:
- Generic record-array detection: explicit record_path -> top-level list ->
whitelist -> bounded auto-detect (list-of-objects, <=3 levels), longest-wins
with a warning on ambiguity.
- Reattach top-level scalar siblings (status, count, ...) as constant columns;
meta_ prefix on name collisions.
- New config args record_path (STR) and auto_explode (STR 'true'/'false',
default 'true'), settable at connection level and overridable per query.
- Preserve legacy behavior: empty whitelist arrays -> empty DataFrame;
auto_explode='false' keeps the single-row shape.
- Net-new unit tests (13 cases) + docs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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
A generic API response shaped as an object with a nested record array collapsed into a single row, with the entire array dumped into one string cell.
Real failure (massive.com snapshot):
{ "tickers": [ …13k objects… ], "status": "OK", "count": 12962 }→ 1 row,tickers= a ~9.8MB string. That blob propagated to a downstream code-execution step and blew the context budget:prompt is too long: 4,656,292 tokens > 1,000,000.The crash is caused by shape, not raw size. The same data as a proper table:
tickerscell4,656,292 tokensRoot cause
format_parsers.py::parse_jsononly picked the record array from a hardcoded whitelist (data/results/items/records/rows/entries). Domain keys liketickersmissed it and fell through to a single-rowjson_normalize.Changes
_resolve_records): explicitrecord_path→ top-level list → whitelist → bounded auto-detect of any list-of-objects (≤3 levels deep). One candidate wins; multiple → longest wins with a warning advisingrecord_path.status,count, …) become constant columns;meta_prefix on name collisions.record_path(STR) andauto_explode(STR'true'/'false', default'true') — settable at connection level and overridable per query in the WHERE clause.{"data": []}) → empty DataFrame;auto_explode='false'keeps the legacy single-row shape.Tests
Net-new
tests/test_format_parsers.py— 13 cases covering the bug, whitelist/list/single-object regressions, list-of-scalars, multi-array warning,record_path(nested + miss),auto_explode=false, dotted columns,meta_collisions, empty array, and primitives. All 13 pass in thedev-mindsdbcontainer.Notes / risk
auto_explode='false'orrecord_path.success=True), which lives on the mktplace side.🤖 Generated with Claude Code