schemagen crashes (RecursionError) on cyclic array $ref targets #33

Closed
opened 2026-06-28 14:33:46 +00:00 by weaselbot · 0 comments
Member

When a $defs array definition references itself (directly or through a chain of only-array definitions), weaseljson_schemagen.py creates a self-referential TArr and then recurses infinitely while rendering C++ types.

Reproduction

{
  "type": "array",
  "items": { "$ref": "#/$defs/Node" },
  "$defs": {
    "Node": {
      "type": "array",
      "items": { "$ref": "#/$defs/Node" }
    }
  }
}

Run:

python3 contrib/schemagen/weaseljson_schemagen.py schema.json -o gen.h --namespace ns

Result:

RecursionError: maximum recursion depth exceeded

The same crash happens for an object field that points to a self-referential array:

{
  "type": "object",
  "properties": {
    "items": { "$ref": "#/$defs/Items" }
  },
  "$defs": {
    "Items": {
      "type": "array",
      "items": { "$ref": "#/$defs/Items" }
    }
  }
}

Root cause

In contrib/schemagen/weaseljson_schemagen.py lines 311-326, build_type for arrays registers the new TArr in self._building before building its items. When the items contain a $ref back to the same definition, build_def returns the cached tuple (t, nullable), so the assignment t.elem = elem makes the array its own element type.

This self-reference then causes infinite recursion in multiple places:

  • base_cpp (lines 473-486) and storage_cpp (lines 489-494) recurse on ty.elem.
  • _walk_arrays (lines 568-573) recurses on ty.elem.

Object-only cycles are handled by break_cycles using std::unique_ptr, but array-only cycles have no object edge for break_cycles to cut.

Expected behavior

Either:

  1. Generate a valid C++ type for recursive arrays (e.g., std::vector<std::unique_ptr<...>>), or
  2. Reject the schema at generation time with a clear GenError instead of crashing.

Actual behavior

Python stack overflow / RecursionError.

Impact

Any schema that models a recursive tree of arrays (e.g., nested lists, s-expressions, filesystem-like trees where every node is an array of nodes) will crash the generator. A crashing generator is also problematic for CI pipelines or web services that accept user-supplied schemas.

When a `$defs` array definition references itself (directly or through a chain of only-array definitions), `weaseljson_schemagen.py` creates a self-referential `TArr` and then recurses infinitely while rendering C++ types. **Reproduction** ```json { "type": "array", "items": { "$ref": "#/$defs/Node" }, "$defs": { "Node": { "type": "array", "items": { "$ref": "#/$defs/Node" } } } } ``` Run: ```sh python3 contrib/schemagen/weaseljson_schemagen.py schema.json -o gen.h --namespace ns ``` Result: ``` RecursionError: maximum recursion depth exceeded ``` The same crash happens for an object field that points to a self-referential array: ```json { "type": "object", "properties": { "items": { "$ref": "#/$defs/Items" } }, "$defs": { "Items": { "type": "array", "items": { "$ref": "#/$defs/Items" } } } } ``` **Root cause** In `contrib/schemagen/weaseljson_schemagen.py` lines 311-326, `build_type` for arrays registers the new `TArr` in `self._building` *before* building its `items`. When the items contain a `$ref` back to the same definition, `build_def` returns the cached tuple `(t, nullable)`, so the assignment `t.elem = elem` makes the array its own element type. This self-reference then causes infinite recursion in multiple places: - `base_cpp` (lines 473-486) and `storage_cpp` (lines 489-494) recurse on `ty.elem`. - `_walk_arrays` (lines 568-573) recurses on `ty.elem`. Object-only cycles are handled by `break_cycles` using `std::unique_ptr`, but array-only cycles have no object edge for `break_cycles` to cut. **Expected behavior** Either: 1. Generate a valid C++ type for recursive arrays (e.g., `std::vector<std::unique_ptr<...>>`), or 2. Reject the schema at generation time with a clear `GenError` instead of crashing. **Actual behavior** Python stack overflow / `RecursionError`. **Impact** Any schema that models a recursive tree of arrays (e.g., nested lists, s-expressions, filesystem-like trees where every node is an array of nodes) will crash the generator. A crashing generator is also problematic for CI pipelines or web services that accept user-supplied schemas.
weaselbot was assigned by andrew 2026-06-30 16:19:15 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: weaselab/weaseljson#33