{"record":{"id":"b56f3c84c28fffd7","repo":"mastra-ai/mastra","slug":"toolcall-compounds-must-be-rendered-within-toolcal","errorCode":null,"errorMessage":"ToolCall compounds must be rendered within ToolCall","messagePattern":"ToolCall compounds must be rendered within ToolCall","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/playground-ui/src/ds/components/ai/tool-call/tool-call.tsx","lineNumber":26,"sourceCode":"import { Shimmer } from '@/ds/components/Shimmer';\nimport { Txt } from '@/ds/components/Txt';\nimport { cn } from '@/lib/utils';\n\ntype ToolCallStatus = 'idle' | 'running' | 'error';\n\ninterface ToolCallContextValue {\n  open: boolean;\n  status: ToolCallStatus;\n}\n\nconst ToolCallContext = createContext<ToolCallContextValue | undefined>(undefined);\n\n// Stryker disable next-line ArrowFunction: the default callback is intentionally behaviorless.\nconst noopOpenChange = () => {};\n\nfunction useToolCall() {\n  const context = useContext(ToolCallContext);\n  if (!context) throw new Error('ToolCall compounds must be rendered within ToolCall');\n  return context;\n}\n\nexport interface ToolCallProps extends Omit<\n  ComponentProps<typeof Collapsible>,\n  'defaultOpen' | 'onOpenChange' | 'open'\n> {\n  open?: boolean;\n  defaultOpen?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  status?: ToolCallStatus;\n}\n\nexport function ToolCall({\n  open: controlledOpen,\n  defaultOpen = false,\n  onOpenChange = noopOpenChange,\n  status = 'idle',","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/playground-ui/src/ds/components/ai/tool-call/tool-call.tsx#L8-L44","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n<ToolCall.Header status={status} /> {/* throws: no root */}\n<ToolCall args={args} />\n\n// after\n<ToolCall {...toolCallProps}>\n  <ToolCall.Header />\n</ToolCall>","handlingStrategy":"validation","validationCode":"const ctx = useContext(ToolCallContext);\nif (!ctx) {\n  // not within <ToolCall>: render with explicit status/open props instead\n}","typeGuard":"const hasToolCallContext = (v: ToolCallContextValue | null): v is ToolCallContextValue => v != null;","tryCatchPattern":"try {\n  const { status, open } = useToolCall();\n  // use status/open\n} catch {\n  // outside <ToolCall>: fall back to props\n}","preventionTips":["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."],"tags":["react","context","compound-components","provider-missing"],"backgroundTag":"missing-react-context-provider","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}