fix(schema): strip null from enum arrays in tool schemas for strict validators#3727
Open
Crazy-LittleBoy wants to merge 6 commits into
Open
fix(schema): strip null from enum arrays in tool schemas for strict validators#3727Crazy-LittleBoy wants to merge 6 commits into
Crazy-LittleBoy wants to merge 6 commits into
Conversation
…alidators Schemars' AddNullable adds "nullable": true and removes "null" from the type keyword, but does not remove null values from enum arrays. This produced invalid JSON Schema (e.g. type:string with null in enum) that strict validators like Moonshot/Kimi reject with 400 Bad Request. Fix at three layers: 1. Domain layer: new NormalizeNullable transform replaces AddNullable in ToolCatalog::schema(). It delegates type normalization to AddNullable, then recursively strips null from enum arrays when nullable is set. 2. App layer: enforce_strict_schema now strips null from enum regardless of strict mode (previously strict-only). This guards external MCP schemas that bypass NormalizeNullable. 3. Anthropic pipeline: EnforceStrictObjectSchema now normalizes tools[*].input_schema via enforce_strict_schema(schema, false), closing a gap where tool schemas were never sanitized. Co-Authored-By: ForgeCode <noreply@forgecode.dev>
Crazy-LittleBoy
force-pushed
the
fix/kimi-null-enum-schema
branch
from
July 21, 2026 05:15
aa0482b to
e43291e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When using Kimi (Moonshot) models via Anthropic-compatible or OpenAI-compatible API endpoints, requests fail with:
Root Cause
schemars'AddNullabletransform adds"nullable": trueand removes"null"from thetypekeyword, but does not removenullvalues fromenumarrays. This produces invalid JSON Schema whereenumcontainsnullwhiletypeis e.g."string"— a contradiction that strict validators (Moonshot/Kimi) reject.The existing cleanup in
enforce_strict_schemaonly ran in strict mode, which is gated to specific provider IDs (FIREWORKS_AI,XAI,kimi_coding, etc.). Generic OpenAI-compatible and Anthropic-compatible endpoints never triggered the cleanup.Fix (3 layers, architectural root cause)
1. Domain Layer —
NormalizeNullabletransformforge_domain/src/tools/definition/tool_definition.rsAddNullableinToolCatalog::schema()typenormalization toAddNullable, then recursively stripsnullfromenumarrays whennullable: trueis setnullablemarker alone fully conveys nullability (OpenAPI 3.0 convention);nullinenumis redundant2. App Layer —
enforce_strict_schemadefense-in-depthenummoved outside thestrict_modegate inforge_app/src/utils.rsNormalizeNullableanyOfconversion (unchanged behavior)nullablemarker preserved3. Anthropic Pipeline — tool schema normalization gap fill
EnforceStrictObjectSchemainforge_app/src/dto/anthropic/transforms/enforce_schema.rsnow normalizestools[*].input_schemaoutput_formatschema, completely skipping tool schemasTesting
forge_domain: 611 tests pass (3 new tests forNormalizeNullable)forge_app: 723 tests pass (2 new tests for non-strict null stripping + Anthropic tool normalization)nullremoved fromoutput_modeenum in tool definition snapshotCo-Authored-By: ForgeCode noreply@forgecode.dev