Skip to content

Nested arrays fail to round-trip: encoder emits a length-1 list-array header over an empty body ([[[]]]) #62

Description

@antrixy

Description

toon-python (0.9.0b1) mishandles nested array-of-array values in both directions. The reference implementation @toon-format/toon (v2.3.0) round-trips [[[]]] correctly, so both facets are specific to toon-python.

Encode: encode([[[]]]) produces a list-array header ([1]:) that claims one item over a body that yields none, so it cannot be decoded — even by toon-python's own decoder. encode([[[]]]) produces:

[1]:
  [1]:
    - [0]:

decode() of that raises:

File ".../toon_format/decoder.py", line 786, in decode_list_array
    raise ToonDecodeError(f"Expected {expected_length} items, but got {len(result)}")
toon_format.decoder.ToonDecodeError: Expected 1 items, but got 0

Decode: decoding the reference (TypeScript) encoding of [[[]]] returns an object instead of an array — the internal list-array markers leak out as keys:

[{"[1]": {}, "-": []}]

The decoder facet is not specific to empty arrays; any nesting depth >= 3 reproduces it (e.g. the reference encoding of [[{"s":"a:b"}]] decodes to [{"[1]": {}, "- s": "a:b"}]).

Boundary (reduced with a differential fuzzer + delta-shrinker):
[] — fine
[[]] — fine
[[[]]] — breaks (minimal reproducer)

Environment:
toon-python (toon_format): 0.9.0b1
reference impl @toon-format/toon: v2.3.0 (round-trips this input correctly)
Python: 3.14

Reproduction Steps

  1. from toon_format import encode, decode
  2. Call encode([[[]]]) — produces:
    [1]:
    [1]:
    - [0]:
  3. Call decode() on that output — raises ToonDecodeError: Expected 1 items, but got 0
    (toon-python cannot decode its own encoding)

Separately, decoding the reference @toon-format/toon encoding of [[[]]] returns
[{"[1]": {}, "-": []}] — a nested array decoded as an object.

Expected Behavior

[[[]]] round-trips unchanged: decode(encode([[[]]])) == [[[]]].
(The reference impl @toon-format/toon does this correctly.)

Actual Behavior

encode([[[]]]) produces:

[1]:
  [1]:
    - [0]:

The outer header says [1]: (one item) but the innermost level is [0]: (zero items).
decode() of that raises:

File ".../toon_format/decoder.py", line 786, in decode_list_array
    raise ToonDecodeError(f"Expected {expected_length} items, but got {len(result)}")
toon_format.decoder.ToonDecodeError: Expected 1 items, but got 0

Separately, decoding the reference @toon-format/toon encoding of [[[]]] returns an
object instead of an array:

[{"[1]": {}, "-": []}]

Environment

toon-python (toon_format): 0.9.0b1
reference impl @toon-format/toon: v2.3.0 (round-trips this input correctly)
Python: 3.14

Additional Context

The decoder facet is not specific to empty arrays; any nesting depth >= 3 reproduces it
(e.g. the reference encoding of [[{"s":"a:b"}]] decodes to [{"[1]": {}, "- s": "a:b"}]).

Boundary, reduced with a differential fuzzer + delta-shrinker:
[] — fine
[[]] — fine
[[[]]] — breaks (minimal reproducer)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions