a2ui-project / a2ui-project/a2ui

[BUG]: non-ASCII data model keys are unreachable from templates in Dart, web_core and Python

Open Beginner friendly
#2,500 1 comment 0 reactions 1 assignee Claimed by @Varun-S10 View on GitHub
P2 status: first-line-handled status: needs review type: bug
Dominant language
TypeScript
Stars
16.4k
Forks
1.3k
Avg merge
2d 13h
Merged PRs (30d)
134

Description

- [x] I have searched the existing issues to make sure this bug has not already been reported.

## Describe the Bug

A data model key containing any non-ASCII character cannot be referenced from a template. `${señor}`, `${café/precio}` and `${日本}` are parse errors in Dart, TypeScript and Python, and parse correctly in Swift.

| template | Dart | TypeScript | Python | Swift |
| --- | --- | --- | --- | --- |
| `${señor}` | error | error | error | `{path: "señor"}` |
| `${café/precio}` | error | error | error | `{path: "café/precio"}` |
| `${日本}` | error | error | error | `{path: "日本"}` |
| `hola ${señor} qué tal` | error | error | error | parses, literals intact |
| `${a_b-c.d}` | ok | ok | ok | ok |

So an agent that names its data with anything but English ASCII produces a surface that renders on iOS and fails everywhere else.

The data model itself has no such limit, which makes the state reachable but undisplayable:

```dart
final m = DataModel({});
m.set('/señor', 'Diego'); // fine
m.get('/señor'); // 'Diego'
ExpressionParser().parse(r'${señor}'); // A2uiExpressionError
```

An agent can write the key, and read it back through `getClientDataModel`, but cannot put it on screen.

## Root cause

The identifier scanners disagree, and the pattern is worth noting: the three that fail carry a hand-written ASCII range, while the one that works uses its language's own Unicode-aware predicate.

- `dart/a2ui_core/lib/src/processing/expressions.dart` — `_isAlnum` tests code units `0x30–0x39`, `0x41–0x5A`, `0x61–0x7A`.
- `renderers/web_core/.../expression_parser.ts` — `isAlnum` tests `c >= 'a' && c <= 'z'` and friends.
- `agent_sdks/python/a2ui_core/.../expression_parser.py` — `is_alnum` tests `"a" <= c <= "z"` and friends.
- `swift/core/Sources/BasicCatalog/Functions/ExpressionParser.swift` — `scanPathOrIdentifier` tests `c.isLetter || c.isNumber`, which is Unicode-aware in Swift.

The ASCII range looks like a literal port of the same three lines between the first three implementations; Swift's reads like the idiomatic translation, and is the one that comes out right.

## Expected Behavior

I would suggest aligning the other three with Swift rather than the reverse:

- The data model is JSON, and JSON object keys are Unicode strings.
- Paths are JSON Pointer (RFC 6901), which places no restriction on the characters in a key.
- Nothing in the v0.9 specification or in `basic_catalog_implementation_guide.md` says an identifier inside `${...}` must be ASCII.
- Restricting it makes part of a legal data model unreachable from the UI, which is the asymmetry shown above.

Each fix is a couple of lines: Dart has `RegExp(r'\p{L}|\p{N}', unicode: true)` or `String.fromCharCode` category checks, TypeScript has `/[\p{L}\p{N}]/u`, Python has `str.isalnum()`.

If instead the intent is that identifiers are ASCII-only, then that belongs in the guide, and Swift should be tightened — but then agents need a documented way to reach a key that ASCII cannot spell.

## Steps to Reproduce

```
${señor}
```

through each implementation's `ExpressionParser.parse`. Full table above.

## Environment Details

- **OS**: macOS
- **Browser/Platform**: Dart 3.12.2, Node.js 25.2.1, Python 3.13, Swift 6.2.1
- **SDK/Package Name & Version**: all four at main (676a899)
- **Protocol Version**: v0.9

## Additional Context

Found by running all four parser implementations over the same corpus, the method used for #2496 and #2498, now extended from two implementations to four. Two other results from the same run:

- On the `null` keyword left open in #2496, Dart is alone: TypeScript, Python and Swift all produce `''`. That is worth weighing in that discussion, though the guide's wording still points the other way.
- The Python parser raises bare `ValueError` for every parse failure, rather than the `A2uiParseError` its own SDK defines in `a2ui/core/exceptions.py`. Since the conformance schema's error categories map onto that hierarchy, a Python harness for a parser suite cannot currently distinguish a protocol error from any other `ValueError`. Filing separately if useful.

Contributor guide

Open the contributing guide

Research direction

The issue points to the identifier scanner in each language's expression parser: dart/a2ui_core/lib/src/processing/expressions.dart, renderers/web_core/.../expression_parser.ts, agent_sdks/python/a2ui_core/.../expression_parser.py, and swift/core/Sources/BasicCatalog/Functions/ExpressionParser.swift. Start by locating the _isAlnum/isAlnum/is_alnum function in each file and replace the ASCII range check with a Unicode-aware one (e.g., str.isalnum() in Python, /[\p{L}\p{N}]/u in TypeScript). Verify the fix by parsing a template like '${señor}' and ensuring it no longer throws an error.

Written by the indexing model from the issue text.

Assessment

Tech stack
dart, python, swift, typescript
Domain
backend-api-design, internationalization
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
75/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.