microsoft / microsoft/typespec
[Bug]: Custom-code references to generated (un-stubbed) types emit empty namespace ( global::.TypeName )
- Dominant language
- Java
- Stars
- 5.9k
- Forks
- 394
- Avg merge
- 1d 23h
- Merged PRs (30d)
- 104
Description
### Describe the bug
Summary
When custom code references a generated type that is not also declared in the customization compilation (no [CodeGenType] stub / partial), the generator emits the type with an empty namespace, e.g.:
IDictionary toolConfigs = default
This produces uncompilable output. Observed in Azure.AI.Extensions.OpenAI for ToolConfig and the enum ResponsesToolSearchExecutionType , surfaced in the generated ExtensionsOpenAIModelFactory .
Root cause
• The customization Roslyn compilation ( SourceInputModel.Customization ) is built from custom files only; generated sources are not part of it.
• Generated types referenced by custom code normally resolve because the library declares a stub (e.g. [CodeGenType("BingGroundingSearchToolParameters")] ). Types without a stub resolve to Roslyn error type symbols.
• TypeSymbolExtensions.GetCSharpType → ConstructCSharpTypeFromSymbol builds a CSharpType from the error symbol. The fully-qualified display string of an unresolved symbol carries no namespace, so:
// TypeSymbolExtensions.cs (~line 204)
string[] pieces = fullyQualifiedName.Split('`')[0].Split('.'); // ["ToolConfig"]
string ns = string.Join('.', pieces.Take(pieces.Length - 1)); // "" (empty)
• The empty namespace flows into a real type used by ModelFactoryProvider (the factory derives parameter types from the model's full constructor, which here lives in custom code), producing global::.ToolConfig .
Why it only shows in the model factory
The generated model itself is in the same namespace, so the writer emits the short name ToolConfig and the empty namespace is invisible. The model factory (with using OpenAI.Responses; ) qualifies the type, exposing the empty namespace.
Affected code
Microsoft.TypeSpec.Generator/src/Utilities/TypeSymbolExtensions.cs — ConstructCSharpTypeFromSymbol (namespace computation for error-type symbols).
Suggested fix
When the symbol is an unresolved error type ( TypeKind.Error ) and the computed namespace is empty, fall back to the output library by name via CodeModelGenerator.Instance.TypeFactory.TypeProvidersByName (already maintained, TypeFactory.cs:29 ) to recover the real CSharpType /namespace. This is a best-effort fallback that triggers only for otherwise-broken symbols, so resolved types are unaffected, and it fixes the class of bug for any library — not just those that add [CodeGenType] stubs.
Current workaround
Add a stub for each affected type in custom code, e.g. [CodeGenType("ToolConfig")] public partial class ToolConfig { } .
### Reproduction
Repro
1. Generated type Foo exists (no [CodeGenType] stub in custom code).
2. A custom-code constructor parameter references Foo (e.g. IDictionary ).
3. That constructor is the full constructor used by the model factory.
4. Generated factory contains global::.Foo .
### Checklist
- [x] Follow our [Code of Conduct](https://github.com/microsoft/typespec/blob/main/CODE_OF_CONDUCT.md)
- [x] Check that there isn't already an issue that request the same bug to avoid creating a duplicate.
- [x] Check that this is a concrete bug. For Q&A open a [GitHub Discussion](https://github.com/Microsoft/typespec/discussions).
- [x] The provided reproduction is a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) of the bug.
Contributor guide
Assessment
This issue has not been assessed yet.