janhq/jan · error · Error
Tool components must be used within Tool
Error message
Tool components must be used within Tool
What it means
useTool reads ToolContext, which is only populated by the Tool component in tool.tsx. If the hook runs outside any Tool ancestor, useContext returns null and the hook throws. This guards against rendering Tool sub-components (header, content, input, toolbar) standalone.
Solutions
- Wrap the sub-components in a <Tool toolCallId={...} ...> collapsible before rendering them.
- Remove the stray useTool-consuming component if the Tool wrapper is no longer needed.
- In tests/stories, add a minimal Tool provider wrapper as the decorator.
- If a truly standalone display is needed, avoid these components and build custom markup.
Example fix
// before
<ToolHeader type={part.type} state={part.state} />
// after
<Tool toolCallId={part.toolCallId}>
<ToolHeader type={part.type} state={part.state} />
</Tool> Defensive patterns
Strategy: type-guard
Validate before calling
const canRenderToolParts = (el: React.ReactNode) => React.isValidElement(el) && (el.type === Tool || isDescendantOfTool(el))
Type guard
const toolContext = useContext(ToolContext)
if (toolContext === null) {
// not inside <Tool>; render fallback or skip
return null
} Try / catch
try {
const ctx = useTool() // note: hooks cannot be wrapped in try/catch; guard instead
} catch (e) {
if (e instanceof Error && e.message.includes('Tool components')) {
return <FallbackToolView />
}
throw e
} Prevention
- Always render Tool sub-components inside <Tool>.
- Add Storybook/test decorators that supply a Tool wrapper.
- Centralize tool UI composition in one component that owns the Tool wrapper.
- Never call useTool in components rendered outside the tool tree (portals keep context; separate roots don't).
When it happens
Trigger: Rendering <ToolHeader>, <ToolContent>, <ToolInput>, <ToolToolbar> or calling useTool() without a wrapping <Tool> component (e.g. in a Storybook story, test, or custom tool UI).
Common situations: Refactoring that moves a tool sub-component out of its Tool parent; composing tool UI inside a chat message renderer that forgot the Tool wrapper; tests rendering sub-components in isolation.
Related errors
- ChainOfThought components must be used within ChainOfThought
- DropDrawer components cannot be rendered outside the…
- Reasoning components must be used within Reasoning
- useSidebar must be used within a SidebarProvider.
- Invalid metadata: context_length not found or invalid
AI-assisted analysis of janhq/jan@7205d770c1 (2026-09-17).
Data as JSON: /api/errors/ad675ea4ad89b953.
Report an issue: GitHub.
Appendix: source
Thrown at web-app/src/components/ai-elements/tool.tsx:57
import { parseCitationsFromToolOutput } from '@/lib/citation-parser'
/** Payloads shorter than this fit the collapsed box, so no expand control. */
const OUTPUT_EXPAND_THRESHOLD = 600
type ToolContextValue = {
isOpen: boolean
setIsOpen: (open: boolean) => void
state: ToolUIPart['state']
toolCallId?: string
messageId?: string
}
const ToolContext = createContext<ToolContextValue | null>(null)
export const useTool = () => {
const context = useContext(ToolContext)
if (!context) {
throw new Error('Tool components must be used within Tool')
}
return context
}
export type ToolProps = ComponentProps<typeof Collapsible> & {
className?: string
state: ToolUIPart['state']
toolCallId?: string
messageId?: string
open?: boolean
defaultOpen?: boolean
onOpenChange?: (open: boolean) => void
}
export const Tool = memo(
({
className,
state,View on GitHub (pinned to 7205d770c1)