mastra-ai/mastra · error · Error
ToolCall compounds must be rendered within ToolCall
Error message
ToolCall compounds must be rendered within ToolCall
What it means
ToolCall is a collapsible compound component whose subcomponents read ToolCallContext (status, open state) through useToolCall. The context defaults to null and is only provided by the root <ToolCall> (a Collapsible), so any compound child rendered outside it throws this error. Without the root, the child cannot know the call's status or open state.
Source
Thrown at packages/playground-ui/src/ds/components/ai/tool-call/tool-call.tsx:26
import { Shimmer } from '@/ds/components/Shimmer';
import { Txt } from '@/ds/components/Txt';
import { cn } from '@/lib/utils';
type ToolCallStatus = 'idle' | 'running' | 'error';
interface ToolCallContextValue {
open: boolean;
status: ToolCallStatus;
}
const ToolCallContext = createContext<ToolCallContextValue | undefined>(undefined);
// Stryker disable next-line ArrowFunction: the default callback is intentionally behaviorless.
const noopOpenChange = () => {};
function useToolCall() {
const context = useContext(ToolCallContext);
if (!context) throw new Error('ToolCall compounds must be rendered within ToolCall');
return context;
}
export interface ToolCallProps extends Omit<
ComponentProps<typeof Collapsible>,
'defaultOpen' | 'onOpenChange' | 'open'
> {
open?: boolean;
defaultOpen?: boolean;
onOpenChange?: (open: boolean) => void;
status?: ToolCallStatus;
}
export function ToolCall({
open: controlledOpen,
defaultOpen = false,
onOpenChange = noopOpenChange,
status = 'idle',View on GitHub (pinned to 75dd419e61)
Solutions
- Render all ToolCall compound children within <ToolCall>.
- Re-add the <ToolCall> root wrapper if a refactor dropped it.
- If status/open must be shared across trees, lift that state up and pass it via props instead of using the compounds elsewhere.
- Check conditional rendering so root and children mount/unmount together.
Example fix
// before
<ToolCall.Header status={status} /> {/* throws: no root */}
<ToolCall args={args} />
// after
<ToolCall {...toolCallProps}>
<ToolCall.Header />
</ToolCall> Defensive patterns
Strategy: validation
Validate before calling
const ctx = useContext(ToolCallContext);
if (!ctx) {
// not within <ToolCall>: render with explicit status/open props instead
} Type guard
const hasToolCallContext = (v: ToolCallContextValue | null): v is ToolCallContextValue => v != null;
Try / catch
try {
const { status, open } = useToolCall();
// use status/open
} catch {
// outside <ToolCall>: fall back to props
} Prevention
- Render ToolCall.Header/Body etc. only as children of <ToolCall>.
- Keep root and compound children mounted/unmounted together.
- For custom layouts, pass status and open explicitly instead of reusing compounds.
- Add the root wrapper to stories and snapshot tests.
When it happens
Trigger: Rendering ToolCall.Header/Body/Trigger-like compounds outside <ToolCall>; conditionally unmounting <ToolCall> while children persist; importing a subcomponent into another view without the root wrapper.
Common situations: Building a custom tool-call list where the header is separated from the root; tests or stories rendering subcomponents in isolation; refactoring the collapsible so the provider is removed but compound children remain.
Related errors
- Plan compound components must be rendered inside <Plan>.
- Comment compounds must be rendered within Comment
- ${componentName} must be used within EnvironmentVariablesEdi
- useJSONSchemaForm must be used within a JSONSchemaForm.Root
- useJSONSchemaFormField must be used within a JSONSchemaForm.
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/b56f3c84c28fffd7.
Report an issue: GitHub.