Skip to content

[BUG]: json_schema_builder throws on a property named $id, so it can't validate a catalog against catalog_definition.json #4188

Open
flutter/genui
#1082

Description

@leoafarias
  • I have searched the existing issues to make sure this bug has not already been reported.

Describe the Bug

json_schema_builder throws a TypeError when a schema has a property named $id, $anchor or $dynamicAnchor and validation has to look up an anchor. That's a $ref to an $anchor, or any $dynamicRef.

I ran into this validating a catalog against the official v1.0 catalog_definition.json. That schema declares a property named $id (line 9), and it checks each component against the JSON Schema 2020-12 meta-schema (lines 229-235), which uses $dynamicRef. So any catalog where a component has properties throws instead of returning validation errors, and I ended up dropping that check from my tests.

The package lives in flutter/genui, which routes issues here.

Steps to Reproduce

Run this with json_schema_builder: 0.1.7. It doesn't need any files or network.

import 'package:json_schema_builder/json_schema_builder.dart';

void main() {
  for (final name in ['name', r'$id', r'$anchor', r'$dynamicAnchor']) {
    final schema = Schema.fromMap({
      'type': 'object',
      'properties': {
        name: {'type': 'string'},
        'child': {r'$ref': '#node'},
      },
      r'$defs': {
        'node': {r'$anchor': 'node', 'type': 'object'},
      },
    });
    try {
      print('$name: ${schema.validateSync({'child': <String, Object?>{}})}');
    } catch (error) {
      print('$name: $error');
    }
  }
}

Expected Behavior

All four lines print [], like name does.

Under properties, the keys are instance property names, not keywords (JSON Schema Core 10.3.2.1), so a property can be called $id. ajv accepts these schemas, and it accepts the catalog below under catalog_definition.json.

Screenshots / Video / Logs

Output of the program:

name: []
$id: type '_Map<String, String>' is not a subtype of type 'String?' in type cast
$anchor: type '_Map<String, String>' is not a subtype of type 'String?' in type cast
$dynamicAnchor: type '_Map<String, String>' is not a subtype of type 'String?' in type cast

The A2UI case. The third catalog is {"catalogId":"https://example.com/c","protocolVersion":"1.0","components":{"X":{"type":"object","properties":{"component":{"const":"X"}}}}}:

  component {type: object} -> valid
  component {description: "x"} -> valid
  component with one property (the shape every real component has) -> THROWS _TypeError: type '_Map<String, dynamic>' is not a subtype of type 'String?' in type cast
    at #0      Schema.$id (package:json_schema_builder/src/schema/schema.dart:458:37) | #1      SchemaValidation._findDynamicAnchorInSchema.visit (package:json_schema_builder/src/schema_validation.dart:1421:48) | #2      SchemaValidation._findDynamicAnchorInSchema.visit (package:json_schema_builder/src/schema_validation.dart:1431:11)

Environment Details

  • OS: macOS 27.0.1
  • Browser/Platform: Dart SDK 3.13.4
  • SDK/Package Name & Version: json_schema_builder 0.1.7 from pub.dev, and flutter/genui main (27f5846). 0.1.0 and 0.1.6 throw too.
  • Protocol Version: v1.0 for the catalog case (a2ui main, 8bf644a). The program above doesn't depend on a protocol version.
  • Agent Framework & LLM Model: n/a

Additional Context

Two functions search a schema for an anchor, and both walk every map in the document as if it were a schema. That includes the map under properties:

They read $id, $anchor and $dynamicAnchor through getters that cast to String?. Under properties the value is a schema, so the cast throws.

The JSON Schema Test Suite has this case for $ref ("property named $ref that is not a reference"), but nothing for these three names, so the package's tests don't cover it. The a2ui_core test that checks catalogs against this schema (_catalogDefinitionErrors) strips the external $refs first, so it doesn't reach this either.

I think the fix is for both functions to only descend into the keywords that hold subschemas. Happy to open a PR on flutter/genui with that and a regression test if that works for you.

Activity

  1. added
    status: needs-triageauto-managed: https://github.com/a2ui-project/a2ui/blob/main/scripts/triage.mjs
    on Nov 12, 2026
  2. polina-c commented on Nov 12, 2026

    @polina-c
    Collaborator

    From triage bot:

    Triage suggestion:

    For humans

    Summary:
    json_schema_builder, which lives in flutter/genui and which a2ui_core depends on, throws a TypeError when a schema has a property named $id, $anchor or $dynamicAnchor and validation looks up an anchor. The v1.0 catalog_definition.json declares a property named $id, so validating a catalog against it throws. The report has a short reproduction and offers a fix.

    Why flagged for triage:
    This issue has external input but no priority label yet.

    Suggestion:
    Set P2, and accept the reporter's offer to fix it in flutter/genui. I read the code at the reported commit and the diagnosis holds: _findAnchor and _findDynamicAnchorInSchema visit every map in the schema, including the map under properties, and read these keywords through getters that cast to String?. P2 rather than P1: the reported impact is on validating a catalog against the component definition in tests and tools, and the reporter has a workaround (dropping that check). gspencergoog is the best reviewer for the genui PR: they wrote 10 of the last 20 commits to json_schema_builder.

    1. Add labels: type: bug, P2, lang: dart, status: first-line-handled

    Reply:

    Thanks for the detailed report and the reproduction. We confirmed that both anchor lookups descend into the map under properties. A PR on flutter/genui that descends only into the keywords that hold subschemas, with a regression test, is welcome.

    To adjust and apply this suggestion, run in your a2ui checkout:

    tools/internal/triage_new/triage_ui.sh https://github.com/a2ui-project/a2ui/issues/4188
    For software
    <a2ui>
    root = TriageSuggestion(
      summary="""`json_schema_builder`, which lives in flutter/genui and which `a2ui_core` [depends on](https://github.com/a2ui-project/a2ui/blob/8bf644a37d680a227296b8c0849a7f4b328b8b73/dart/a2ui_core/pubspec.yaml#L30), throws a `TypeError` when a schema has a property named `$id`, `$anchor` or `$dynamicAnchor` and validation looks up an anchor. The v1.0 [catalog_definition.json](https://github.com/a2ui-project/a2ui/blob/8bf644a37d680a227296b8c0849a7f4b328b8b73/specification/v1_0/json/catalog_definition.json#L9) declares a property named `$id`, so validating a catalog against it throws. The report has a short reproduction and offers a fix.""",
      reason="""This issue has external input but no priority label yet.""",
      suggestion="""Set P2, and accept the reporter's offer to fix it in flutter/genui. I read the code at the reported commit and the diagnosis holds: [`_findAnchor`](https://github.com/flutter/genui/blob/27f5846441693fba6da224e3770453f269a7121c/packages/json_schema_builder/lib/src/schema_registry.dart#L239-L275) and [`_findDynamicAnchorInSchema`](https://github.com/flutter/genui/blob/27f5846441693fba6da224e3770453f269a7121c/packages/json_schema_builder/lib/src/schema_validation.dart#L1409-L1442) visit every map in the schema, including the map under `properties`, and read these keywords through [getters](https://github.com/flutter/genui/blob/27f5846441693fba6da224e3770453f269a7121c/packages/json_schema_builder/lib/src/schema/schema.dart#L447-L458) that cast to `String?`. P2 rather than P1: the reported impact is on validating a catalog against the [component definition](https://github.com/a2ui-project/a2ui/blob/8bf644a37d680a227296b8c0849a7f4b328b8b73/specification/v1_0/json/catalog_definition.json#L229-L235) in tests and tools, and the reporter has a workaround (dropping that check). `gspencergoog` is the best reviewer for the genui PR: they wrote [10 of the last 20 commits](https://github.com/flutter/genui/commits/main/packages/json_schema_builder) to `json_schema_builder`.""",
      reply="""Thanks for the detailed report and the reproduction. We confirmed that both anchor lookups descend into the map under `properties`. A PR on flutter/genui that descends only into the keywords that hold subschemas, with a regression test, is welcome.""",
      added_labels=["type: bug", "P2", "lang: dart", "status: first-line-handled"]
    )
    </a2ui>
    

    🤖 Agent-generated

  3. added
    type: bugSomething isn't working
    P2The team intends to work on this in the near future, or at a lower priority.
    and removed
    status: needs-triageauto-managed: https://github.com/a2ui-project/a2ui/blob/main/scripts/triage.mjs
    on Nov 12, 2026
  4. self-assigned this
    on Nov 12, 2026
  5. Varun-S10 commented on Nov 12, 2026

    @Varun-S10
    Collaborator

    Hi @leoafarias, Thank you for raising the issue. We have reproduced the issue and we appreciate you have raised pr to fix the issue. The pr will be reviewed. Thank you for your patience and understanding.

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions