Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

.Net: Python: KernelJsonSchemaBuilder emits {"type": "object"} with no enum for typing.Literal parameters and fields

Open Beginner friendly
#14,511 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 4 days

@VANDRANKI is already working on this.

Since Sep 30, 2026.

  • #14514 by @VANDRANKI — open

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
82/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
python
Domain
api

Research direction

Read python/semantic_kernel/schema/kernel_json_schema_builder.py, focusing on handle_complex_type() and get_json_schema(). Run the direct KernelJsonSchemaBuilder.build() examples and the provided kernel-function reproduction first. Done means Literal values produce typed enum schemas, including nullable and list-nested cases, rather than object schemas.

Written by the indexing model from the issue text.

Description

.NET python triage

What happens

KernelJsonSchemaBuilder has no handling for typing.Literal. A parameter typed Literal["fast", "slow"] is advertised to the model as a bare {"type": "object"} with no enum. The model is told the argument is an object and is never told the allowed values.

This affects @kernel_function parameters, Pydantic model fields, and Literal nested inside list[...] or Optional[...].

Where

python/semantic_kernel/schema/kernel_json_schema_builder.py, KernelJsonSchemaBuilder.handle_complex_type() and get_json_schema().

Literal[...] has __args__, so build() sends it to handle_complex_type(). That method handles list, set, dict, tuple and Union, but not Literal, so it reaches the final fallback:

schema = cls.get_json_schema(parameter_type)   # TYPE_MAPPING.get(Literal[...], "object")

TYPE_MAPPING has no entry for a Literal alias, so the default "object" is returned. build_enum_schema() already produces the right shape for Enum classes ({"type": ..., "enum": [...]}), but nothing does that for Literal.

Repro

Tested with semantic-kernel==1.44.1. I diffed kernel_json_schema_builder.py against current main and the file is identical.

import json
from typing import Annotated, Literal
from pydantic import BaseModel
from semantic_kernel import Kernel
from semantic_kernel.functions import kernel_function
from semantic_kernel.connectors.ai.function_calling_utils import kernel_function_metadata_to_function_call_format

class Opts(BaseModel):
    level: Literal["low", "high"]

class Plugin:
    @kernel_function(name="run")
    def run(self, mode: Annotated[Literal["fast", "slow"], "speed mode"], opts: Annotated[Opts, "options"]) -> str:
        return mode

k = Kernel(); k.add_plugin(Plugin(), "p")
fn = k.get_function("p", "run")
print(json.dumps(kernel_function_metadata_to_function_call_format(fn.metadata)["function"]["parameters"], indent=1))

Output:

{
 "type": "object",
 "properties": {
  "mode": {"type": "object", "description": "speed mode"},
  "opts": {
   "type": "object",
   "properties": {"level": {"type": "object"}},
   "required": ["level"],
   "description": "options"
  }
 },
 "required": ["mode", "opts"]
}

Direct builder calls:

KernelJsonSchemaBuilder.build(Literal["fast", "slow"])            # {'type': 'object'}
KernelJsonSchemaBuilder.build(Optional[Literal["fast", "slow"]])  # {'type': ['object', 'null']}
KernelJsonSchemaBuilder.build(list[Literal["a", "b"]])            # {'type': 'array', 'items': {'type': 'object'}}

Expected

{"type": "string", "enum": ["fast", "slow"], "description": "speed mode"}

This is the same shape build_enum_schema() already returns for an Enum subclass. Optional[Literal[...]] should be nullable and list[Literal[...]] should have an enum on items.

Actual

{"type": "object"} with no allowed values. The model can emit any value, including a JSON object, and the kernel then tries to bind it to a str parameter. Tools that use Literal to constrain choices (a very common pattern) lose that constraint entirely.

Suggested fix

Add a Literal branch in handle_complex_type():

if origin is Literal:
    values = list(args)
    types = {TYPE_MAPPING.get(type(v), "string") for v in values}
    schema = {"type": types.pop() if len(types) == 1 else sorted(types), "enum": values}
    if description:
        schema["description"] = description
    return schema

The Optional[...] branch also needs to keep working with an enum schema (add None to the enum when making it nullable).

I searched open and closed issues and PRs for Literal, KernelJsonSchemaBuilder, and schema builder problems. #14239, #14241 and #14310 are about string forward references and only make sure Literal values are left alone while resolving them. #14482 and #14155 are about NoneType in unions. None of them adds Literal support.

Dominant language
C#
Stars
28.6k
Forks
4.8k
Avg merge
1d 15h
Merged PRs (30d)
12

Getting set up

Open in Codespaces

Starts the project's dev container in your browser, under your own GitHub account.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from microsoft/semantic-kernel

All issues in microsoft/semantic-kernel

Similar issues

More C# issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.