Description
Reference-preserving code-model JSON can fail to deserialize when a shared object is first serialized inside a decorator argument and referenced later from the main code-model graph.
For example, the serialized shape can effectively contain:
{
"decorators": [
{
"name": "example",
"arguments": {
"value": {
"$id": "2463",
"name": "SharedModel"
}
}
}
],
"models": [
{
"$ref": "2463"
}
]
}
The C# code-model reader currently captures decorator arguments as opaque JSON/BinaryData. Because those nested values are not deserialized through the shared TypeSpec reference resolver, $id: 2463 is never registered. Deserialization later reaches $ref: 2463 in the ordinary model graph and fails with an error similar to:
System.Text.Json.JsonException: cannot resolve reference 2463
We encountered this while upgrading the Azure management-plane C# generator. The immediate SDK-side mitigation removes model-valued arguments from known decorators before serializing the code model, but this is fragile: new decorators can reintroduce the same graph shape, and consumers may legitimately need those arguments.
Suggested change
Decorator arguments should participate in the same reference-resolution pass as the rest of the code model. Possible approaches include:
- Deserialize decorator argument values through the shared
TypeSpecReferenceResolver rather than capturing their raw JSON directly.
- Pre-scan the complete JSON document to register all
$id nodes before resolving $ref nodes, including IDs nested under opaque or extensibility properties.
- Preserve raw decorator JSON only after reference metadata has been registered or normalized, so opaque payload handling cannot hide graph identities from the resolver.
The desired behavior is that a $ref remains resolvable regardless of where the corresponding $id first appears in the serialized document.
Impact
This failure blocked regeneration of multiple Azure management SDK libraries after a dependency upgrade. Sanitizing decorator arguments avoids the immediate exception but loses metadata and does not address the underlying deserializer behavior.
Description
Reference-preserving code-model JSON can fail to deserialize when a shared object is first serialized inside a decorator argument and referenced later from the main code-model graph.
For example, the serialized shape can effectively contain:
{ "decorators": [ { "name": "example", "arguments": { "value": { "$id": "2463", "name": "SharedModel" } } } ], "models": [ { "$ref": "2463" } ] }The C# code-model reader currently captures decorator arguments as opaque JSON/BinaryData. Because those nested values are not deserialized through the shared TypeSpec reference resolver,
$id: 2463is never registered. Deserialization later reaches$ref: 2463in the ordinary model graph and fails with an error similar to:We encountered this while upgrading the Azure management-plane C# generator. The immediate SDK-side mitigation removes model-valued arguments from known decorators before serializing the code model, but this is fragile: new decorators can reintroduce the same graph shape, and consumers may legitimately need those arguments.
Suggested change
Decorator arguments should participate in the same reference-resolution pass as the rest of the code model. Possible approaches include:
TypeSpecReferenceResolverrather than capturing their raw JSON directly.$idnodes before resolving$refnodes, including IDs nested under opaque or extensibility properties.The desired behavior is that a
$refremains resolvable regardless of where the corresponding$idfirst appears in the serialized document.Impact
This failure blocked regeneration of multiple Azure management SDK libraries after a dependency upgrade. Sanitizing decorator arguments avoids the immediate exception but loses metadata and does not address the underlying deserializer behavior.