sgl-project/sglang · error · ValueError

Kimi K3 tool property schemas must be JSON schemas

Error message

Kimi K3 tool property schemas must be JSON schemas

What it means

Each entry in tool.parameters.properties must be a property name (str) mapped to a JSON schema (bool for true/false schemas, or an object). A key or value of any other type (e.g. a schema given as a string or list) cannot be converted into a tag format, so the strict builder raises.

Source

Thrown at python/sglang/srt/function_call/kimik3_structural_tag.py:399

    if not isinstance(properties, dict):
        raise ValueError("Kimi K3 tool parameters 'properties' must be an object")
    required = parameters.get("required", [])
    if not isinstance(required, list) or not all(
        isinstance(item, str) for item in required
    ):
        raise ValueError("Kimi K3 tool parameters 'required' must be a string list")

    required_set = set(required)
    missing = required_set.difference(properties)
    if missing:
        raise ValueError(
            f"Kimi K3 required parameters are missing schemas: {sorted(missing)!r}"
        )

    elements: List[Format] = []
    for key, schema in properties.items():
        if not isinstance(key, str) or not isinstance(schema, (bool, dict)):
            raise ValueError("Kimi K3 tool property schemas must be JSON schemas")
        argument = _known_argument_format(key, schema, parameters)
        if argument is None:
            if key in required_set:
                raise ValueError(
                    f"Kimi K3 required parameter {key!r} accepts no values"
                )
            continue
        elements.append(
            argument if key in required_set else OptionalFormat(content=argument)
        )

    additional = parameters.get("additionalProperties", True)
    if additional is True:
        elements.append(StarFormat(content=_dynamic_argument_format(True, parameters)))
    elif isinstance(additional, dict):
        elements.append(
            StarFormat(content=_dynamic_argument_format(additional, parameters))
        )

View on GitHub (pinned to 0132848349)

Solutions

  1. Wrap bare types into proper schema objects: {"type": "string"}
  2. Ensure every key is a string and every value is a dict or boolean
  3. Run the tool schema through a JSON Schema validator before passing it to the server

Example fix

// before
"properties": {"city": "string"}
// after
"properties": {"city": {"type": "string"}}
Defensive patterns

Strategy: validation

Validate before calling

for k, v in params.get("properties", {}).items():
    assert isinstance(k, str) and isinstance(v, (bool, dict)), f"bad property {k}"

Type guard

def valid_property_schemas(params: dict) -> bool:
    return all(
        isinstance(k, str) and isinstance(v, (bool, dict))
        for k, v in params.get("properties", {}).items()
    )

Try / catch

try:
    build_tag(tools)
except ValueError as e:
    if "must be JSON schemas" in str(e):
        wrap_bare_types(tools)
    else:
        raise

Prevention

When it happens

Trigger: A properties entry like "city": "string" (bare type string) or a non-string key, when building the Kimi K3 structural tag.

Common situations: Shorthand schemas from tutorials ("type": "string" written as just "string"); tool definitions produced by LLMs that inline type names; serialization that turns schema dicts into strings.

Related errors


AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28). Data as JSON: /api/errors/21d9d69e2aa8ad2c. Report an issue: GitHub.