← Blog

Claude API Function Calling Schema Validation Guide

2026-10-11 · 4 min read · SubToAPI Team

Claude's function calling (tool use) feature lets the model return structured JSON that maps to a function you define. The problem developers hit almost immediately: Claude generates arguments that are syntactically valid JSON but don't actually match your expected schema — a missing required field, a string where you expected a number, an extra property, or an enum value that doesn't exist in your list. Schema validation is the step between "Claude returned something" and "my code can safely execute this."

The short answer: define your tool input schema using JSON Schema, validate every tool_use response against it with a library like Zod, Ajv, or Pydantic before you execute anything, and return validation errors back to Claude as a tool_result so it can self-correct. Claude does not guarantee schema conformance just because you declared one — it's a strong prior, not a hard constraint — so validation is not optional if you're calling real functions with real side effects.

Why Schema Validation Matters for Tool Use

When you define a tool, you pass an input_schema describing the expected shape of the arguments:

{
  "name": "get_weather",
  "description": "Get current weather for a location",
  "input_schema": {
    "type": "object",
    "properties": {
      "location": { "type": "string" },
      "unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
    },
    "required": ["location"]
  }
}

This schema guides generation, but it's a prompt-level constraint, not a type system. Claude can still return {"location": "Berlin", "unit": "Kelvin"} or omit location entirely if the conversation context is ambiguous. If your backend passes that straight into a database query, a payment call, or a shell command, you're trusting unverified input from a model output — the same risk class as trusting unverified user input.

A Practical Validation Pipeline

The pattern that works reliably in production:

  1. Define the schema once, in a format usable both for the Claude tool declaration and for runtime validation (JSON Schema is ideal because most validators consume it directly).
  2. Parse the tool_use block from the response.
  3. Validate before execution. Reject or repair invalid input.
  4. On failure, send a tool_result with is_error: true describing what was wrong, so Claude can retry with corrected arguments.

Here's a minimal Node.js example using Ajv:

import Ajv from "ajv";

const ajv = new Ajv();
const schema = {
  type: "object",
  properties: {
    location: { type: "string" },
    unit: { type: "string", enum: ["celsius", "fahrenheit"] }
  },
  required: ["location"],
  additionalProperties: false
};

const validate = ajv.compile(schema);

function handleToolUse(toolUseBlock) {
  const valid = validate(toolUseBlock.input);
  if (!valid) {
    return {
      type: "tool_result",
      tool_use_id: toolUseBlock.id,
      content: `Invalid arguments: ${ajv.errorsText(validate.errors)}`,
      is_error: true
    };
  }
  return executeGetWeather(toolUseBlock.input);
}

The additionalProperties: false flag matters more than it looks — it's your defense against Claude inventing extra fields that silently get ignored by your function but could indicate a misunderstanding of the tool's purpose.

Using Zod or Pydantic Instead

If you're already using TypeScript, Zod gives you both compile-time types and runtime validation from one definition:

import { z } from "zod";

const WeatherArgs = z.object({
  location: z.string().min(1),
  unit: z.enum(["celsius", "fahrenheit"]).optional()
});

function handleToolUse(toolUseBlock) {
  const result = WeatherArgs.safeParse(toolUseBlock.input);
  if (!result.success) {
    return buildErrorResult(toolUseBlock.id, result.error.message);
  }
  return executeGetWeather(result.data);
}

In Python, Pydantic models serve the same purpose and can even generate the JSON Schema you pass to Claude's input_schema, keeping the two in sync automatically instead of maintaining duplicate schema definitions by hand.

Common Schema Mismatches to Guard Against

Retrying Gracefully

Returning a clear, specific error message in the tool_result block is what lets Claude actually fix the problem instead of repeating the same mistake. Vague messages like "invalid input" produce vague retries. Messages like "unit must be one of: celsius, fahrenheit — received 'Kelvin'" give the model exactly what it needs to correct course on the next turn. For details on structuring these conversation turns, see /docs/tools.

Where SubToAPI Fits

If you're already managing Claude access across a team and want tool-use traffic flowing through a single, observable endpoint, SubToAPI turns your Claude access into a standard HTTPS API with per-application keys (sub_live_...), streaming, and usage metadata — so you can see which app or key is generating malformed tool calls without digging through raw logs. Check /docs/tools for request formats, or /docs/quickstart to get a key issued in minutes. Plans start with a free trial at /signup, with pricing details at /pricing.

FAQ

Does Claude guarantee its function call arguments match my input_schema? No. The schema strongly influences generation but isn't enforced like a type system. Always validate tool_use input at runtime before executing any function with real side effects.

What's the best library for validating Claude tool call arguments? Ajv or Zod for JavaScript/TypeScript, Pydantic for Python. All three can derive from or generate JSON Schema, so you can share one schema definition between the Claude tool declaration and your validator.

What should I do when validation fails? Return a tool_result block with is_error: true and a specific, actionable error message. This lets Claude retry with corrected arguments on the next turn instead of failing silently or crashing your backend.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →