microsoft / microsoft/typespec

[Bug]: Custom-code references to generated (un-stubbed) types emit empty namespace ( global::.TypeName )

Open
#11,060 1 comment 2 reactions 2 assignees Claimed by @JoshLove-msft View on GitHub
bug emitter:client:csharp
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.