diff --git a/.chronus/changes/fix-http-server-csharp-template-parameter-2026-03-04.md b/.chronus/changes/fix-http-server-csharp-template-parameter-2026-03-04.md new file mode 100644 index 00000000000..c574fda1eb7 --- /dev/null +++ b/.chronus/changes/fix-http-server-csharp-template-parameter-2026-03-04.md @@ -0,0 +1,7 @@ +--- +changeKind: fix +packages: + - "@typespec/http-server-csharp" +--- + +Fix crash when emitting interfaces that contain template operations. Template operations (e.g. `getItem(): T`) within interfaces will simply be skipped when emitting the interface. diff --git a/packages/http-server-csharp/src/lib/service.ts b/packages/http-server-csharp/src/lib/service.ts index 6f9b671f398..79e6863db81 100644 --- a/packages/http-server-csharp/src/lib/service.ts +++ b/packages/http-server-csharp/src/lib/service.ts @@ -784,6 +784,7 @@ export async function $onEmit(context: EmitContext) usings: [], }; for (const [name, operation] of iface.operations) { + if (isTemplateDeclaration(operation)) continue; const doc = getDoc(this.emitter.getProgram(), operation); const returnTypes: Type[] = []; const [httpOp, _] = getHttpOperation(this.emitter.getProgram(), operation); @@ -1004,7 +1005,12 @@ export async function $onEmit(context: EmitContext) .checker.cloneType(responseType, { name: modelName }); responseType = returnedType; } - this.emitter.emitType(responseType); + // TemplateParameter types cannot be emitted (they are unresolved template placeholders). + // Template operations are filtered in interfaceDeclarationOperations, but this guard + // prevents crashes if a TemplateParameter response type is encountered via other paths. + if (responseType.kind !== "TemplateParameter") { + this.emitter.emitType(responseType); + } const context = this.emitter.getContext(); const result = getCSharpType(this.emitter.getProgram(), responseType, context.namespace); diff --git a/packages/http-server-csharp/test/generation.test.ts b/packages/http-server-csharp/test/generation.test.ts index 7f5c0175c66..df2d7201792 100644 --- a/packages/http-server-csharp/test/generation.test.ts +++ b/packages/http-server-csharp/test/generation.test.ts @@ -1245,6 +1245,23 @@ it("Handles user-defined model templates", async () => { ); }); +it("Handles template operations in interfaces without crashing", async () => { + // Regression test: interfaces with template operations should not crash with + // "Encountered type TemplateParameter which we don't know how to emit." + // Template operations (e.g. getItem(): T) should be skipped during emission. + await compileAndValidateMultiple( + tester, + ` + interface MyOps { + @get list(): string; + @get getItem(): T; + } + `, + [["IMyOps.cs", ["interface IMyOps", "Task ListAsync( );"]]], + [["IMyOps.cs", ["GetItemAsync"]]], + ); +}); + it("Handles void type in operations", async () => { await compileAndValidateMultiple( tester,