Skip to content

Generating C# code fore Record<T> returns empty model #7203

@42ama

Description

@42ama

What are you generating using Kiota, clients or plugins?

API Client/SDK

In what context or format are you using Kiota?

Mac executable

Client library/SDK language

Csharp

Describe the bug

I want to generate a TypeSpec client in which the output model is described using Record<T>. However, when generating the class, the data model description disappears — only an empty class remains.

Expected behavior

I expect the generated client description to correctly reflect the service, including the defined models.

How to reproduce

  1. Create new tsp project with specified typespec file
  2. Run tsp compile .
  3. Run kiota:
kiota generate \
    --language CSharp \
    --openapi tsp-output/@typespec/openapi3/openapi.MyApp.yaml \
    --output ./kiota-output \
    --namespace-name MyService \
    --class-name MyServiceClient \
    --exclude-backward-compatible \
    --clean-output
Typespec with Record
import "@typespec/http";
import "@typespec/openapi3";

using TypeSpec.Http;

@service(#{ title: "MyApp" })
namespace MyApp {
  @tag("TestEndpointApi")
  interface TestEndpointApi {
    @route("/test-endpoint/")
    @get
    get(): Record<TestModel>;
  }

  model TestModel {
    name: string;
    phone?: string;
  }
}
Generated C# for Record

For models only GetResponse generated without reference to expected name and phone properties.

// <auto-generated/>
#pragma warning disable CS0618
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using System.Collections.Generic;
using System.IO;
using System;
namespace MyService.TestEndpoint
{
    [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
    #pragma warning disable CS1591
    public partial class GetResponse : IAdditionalDataHolder, IParsable
    #pragma warning restore CS1591
    {
        /// <summary>Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well.</summary>
        public IDictionary<string, object> AdditionalData { get; set; }
        /// <summary>
        /// Instantiates a new <see cref="global::MyService.TestEndpoint.GetResponse"/> and sets the default values.
        /// </summary>
        public GetResponse()
        {
            AdditionalData = new Dictionary<string, object>();
        }
        /// <summary>
        /// Creates a new instance of the appropriate class based on discriminator value
        /// </summary>
        /// <returns>A <see cref="global::MyService.TestEndpoint.GetResponse"/></returns>
        /// <param name="parseNode">The parse node to use to read the discriminator value and create the object</param>
        public static global::MyService.TestEndpoint.GetResponse CreateFromDiscriminatorValue(IParseNode parseNode)
        {
            _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode));
            return new global::MyService.TestEndpoint.GetResponse();
        }
        /// <summary>
        /// The deserialization information for the current model
        /// </summary>
        /// <returns>A IDictionary&lt;string, Action&lt;IParseNode&gt;&gt;</returns>
        public virtual IDictionary<string, Action<IParseNode>> GetFieldDeserializers()
        {
            return new Dictionary<string, Action<IParseNode>>
            {
            };
        }
        /// <summary>
        /// Serializes information the current object
        /// </summary>
        /// <param name="writer">Serialization writer to use to serialize this model</param>
        public virtual void Serialize(ISerializationWriter writer)
        {
            _ = writer ?? throw new ArgumentNullException(nameof(writer));
            writer.WriteAdditionalData(AdditionalData);
        }
    }
}
#pragma warning restore CS0618
Typespec without Record
import "@typespec/http";
import "@typespec/openapi3";

using TypeSpec.Http;

@service(#{ title: "MyApp" })
namespace MyApp {
  @tag("TestEndpointApi")
  interface TestEndpointApi {
    @route("/test-endpoint/")
    @get
    get(): TestModel; // <-- only change is here
  }

  model TestModel {
    name: string;
    phone?: string;
  }
}
Generated C# without Record

Instead of GetResponse - TestModel.cs is generated, which have expected properties.

// <auto-generated/>
#pragma warning disable CS0618
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using System.Collections.Generic;
using System.IO;
using System;
namespace MyService.Models
{
    [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
    #pragma warning disable CS1591
    public partial class TestModel : IAdditionalDataHolder, IParsable
    #pragma warning restore CS1591
    {
        /// <summary>Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well.</summary>
        public IDictionary<string, object> AdditionalData { get; set; }
        /// <summary>The name property</summary>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
        public string? Name { get; set; }
#nullable restore
#else
        public string Name { get; set; }
#endif
        /// <summary>The phone property</summary>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
        public string? Phone { get; set; }
#nullable restore
#else
        public string Phone { get; set; }
#endif
        /// <summary>
        /// Instantiates a new <see cref="global::MyService.Models.TestModel"/> and sets the default values.
        /// </summary>
        public TestModel()
        {
            AdditionalData = new Dictionary<string, object>();
        }
        /// <summary>
        /// Creates a new instance of the appropriate class based on discriminator value
        /// </summary>
        /// <returns>A <see cref="global::MyService.Models.TestModel"/></returns>
        /// <param name="parseNode">The parse node to use to read the discriminator value and create the object</param>
        public static global::MyService.Models.TestModel CreateFromDiscriminatorValue(IParseNode parseNode)
        {
            _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode));
            return new global::MyService.Models.TestModel();
        }
        /// <summary>
        /// The deserialization information for the current model
        /// </summary>
        /// <returns>A IDictionary&lt;string, Action&lt;IParseNode&gt;&gt;</returns>
        public virtual IDictionary<string, Action<IParseNode>> GetFieldDeserializers()
        {
            return new Dictionary<string, Action<IParseNode>>
            {
                { "name", n => { Name = n.GetStringValue(); } },
                { "phone", n => { Phone = n.GetStringValue(); } },
            };
        }
        /// <summary>
        /// Serializes information the current object
        /// </summary>
        /// <param name="writer">Serialization writer to use to serialize this model</param>
        public virtual void Serialize(ISerializationWriter writer)
        {
            _ = writer ?? throw new ArgumentNullException(nameof(writer));
            writer.WriteStringValue("name", Name);
            writer.WriteStringValue("phone", Phone);
            writer.WriteAdditionalData(AdditionalData);
        }
    }
}
#pragma warning restore CS0618

Open API description file

openapi.MyApp-with-Record.yaml

openapi.MyApp-without-Record.yaml

Kiota Version

1.28.0+57130b1b1db3bc5c060498682f41e20c8ae089f2

Latest Kiota version known to work for scenario above?(Not required)

No response

Known Workarounds

No response

Configuration

  • macOS Sequoia 15.7.1
  • ARM

Debug output

Click to expand log ``` kiota generate \ --language CSharp \ --openapi tsp-output/@typespec/openapi3/openapi.MyApp.yaml \ --output ./kiota-output \ --namespace-name MyService \ --class-name MyServiceClient \ --exclude-backward-compatible \ --clean-output \ --log-level Debug info: Kiota.Builder.KiotaBuilder[0] Cleaning output directory .../get-all-single-model-kiota-case/./kiota-output dbug: Kiota.Builder.KiotaBuilder[0] kiota version 1.28.0 info: Kiota.Builder.KiotaBuilder[0] loaded description from local source dbug: Kiota.Builder.KiotaBuilder[0] step 1 - reading the stream - took 00:00:00.0059196 warn: Kiota.Builder.KiotaBuilder[0] OpenAPI warning: #/ - A servers entry (v3) or host + basePath + schemes properties (v2) was not present in the OpenAPI description. The root URL will need to be set manually with the request adapter. dbug: Kiota.Builder.KiotaBuilder[0] step 2 - parsing the document - took 00:00:00.0581297 dbug: Kiota.Builder.KiotaBuilder[0] step 3 - updating generation configuration from kiota extension - took 00:00:00.0000616 dbug: Kiota.Builder.KiotaBuilder[0] step 4 - filtering API paths with patterns - took 00:00:00.0029906 warn: Kiota.Builder.KiotaBuilder[0] No server url found in the OpenAPI document. The base url will need to be set when using the client. dbug: Kiota.Builder.KiotaBuilder[0] step 5 - checking whether the output should be updated - took 00:00:00.0097135 dbug: Kiota.Builder.KiotaBuilder[0] step 6 - create uri space - took 00:00:00.0018198 dbug: Kiota.Builder.KiotaBuilder[0] InitializeInheritanceIndex 00:00:00.0018850 dbug: Kiota.Builder.KiotaBuilder[0] CreateRequestBuilderClass 00:00:00 dbug: Kiota.Builder.KiotaBuilder[0] MapTypeDefinitions 00:00:00.0017514 dbug: Kiota.Builder.KiotaBuilder[0] TrimInheritedModels 00:00:00 dbug: Kiota.Builder.KiotaBuilder[0] CleanUpInternalState 00:00:00 dbug: Kiota.Builder.KiotaBuilder[0] step 7 - create source model - took 00:00:00.0301582 dbug: Kiota.Builder.KiotaBuilder[0] 12ms: Language refinement applied dbug: Kiota.Builder.KiotaBuilder[0] step 8 - refine by language - took 00:00:00.0131009 dbug: Kiota.Builder.KiotaBuilder[0] step 9 - writing files - took 00:00:00.0194416 info: Kiota.Builder.KiotaBuilder[0] loaded description from local source dbug: Kiota.Builder.KiotaBuilder[0] step 10 - writing lock file - took 00:00:00.0114447 Generation completed successfully dbug: Kiota.Builder.KiotaBuilder[0] Api manifest path: .../get-all-single-model-kiota-case/apimanifest.json ```

Other information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    CsharpPull requests that update .net codestatus:waiting-for-triageAn issue that is yet to be reviewed or assignedtype:bugA broken experience

    Type

    No type

    Projects

    Status

    Needs Triage 🔍

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions