[BUG] TS2322: Type BetaRunnableTool<{}> is not assignable to type ToolUnion

Resolved 💬 2 comments Opened Apr 23, 2026 by marc-wilson Closed Apr 23, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

The Ring 5 documentation introduces betaZodTool as the idiomatic TypeScript way to define tools with Zod schemas. However, the return type of betaZodTool (BetaRunnableTool<T>) is not assignable to ToolUnion, which is the type required by the tools array on messages.stream and messages.create.

This causes a TS2322 type error for developers who follow the docs and then try to use the tool with the standard messages API.

SDK version: 0.91.0

Reproduction

Taken directly from the Ring 5 docs snippet:

import Anthropic from "@anthropic-ai/sdk";
import { betaZodTool } from "@anthropic-ai/sdk/helpers/beta/zod";
import { z } from "zod";

const client = new Anthropic();

const createCalendarEvent = betaZodTool({
  name: "create_calendar_event",
  description: "Create a calendar event with attendees and optional recurrence.",
  inputSchema: z.object({
    title: z.string(),
    start: z.string().datetime(),
    end: z.string().datetime(),
  }),
  run: async (input) => {
    return JSON.stringify({ event_id: "evt_123", status: "created", title: input.title });
  },
});

// TS2322: Type 'BetaRunnableTool<{}>' is not assignable to type 'ToolUnion'
client.messages.stream({
  model: "claude-opus-4-6",
  max_tokens: 1024,
  tools: [createCalendarEvent],
  messages: [{ role: "user", content: "Schedule a meeting" }],
});

Workaround

Yes, you can cast:

tools: [createCalendarEvent as unknown as Anthropic.Tool]

But this is a hack, not a solution. It silently throws away the type safety that betaZodTool and Zod are supposed to provide in the first place. The whole point of using betaZodTool is to get typed, schema-validated tool definitions — a cast defeats that entirely and leaves you no better off than writing the raw JSON schema by hand.

Notes

The Ring 5 docs do show the correct usage with client.beta.messages.toolRunner(). The problem is there's nothing in the types or docs that warns developers away from using betaZodTool with messages.stream, which is a natural thing to try.

What Should Happen?

Either:

  1. BetaRunnableTool<T> is accepted by messages.stream / messages.create, or
  2. The docs and/or type signatures make it clear that betaZodTool is only for use with client.beta.messages.toolRunner(), not the standard messages API — so developers know they need a different approach for messages.stream

Error Messages/Logs

Steps to Reproduce

Claude Model

None

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.98

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

_No response_

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗