janhq/jan · error · Error
ChainOfThought components must be used within ChainOfThought
Error message
ChainOfThought components must be used within ChainOfThought
What it means
useChainOfThought is a React context hook that reads ChainOfThoughtContext, which is only populated by the ChainOfThought provider component. If the context value is null (hook called outside a <ChainOfThought> wrapper), the hook throws this Error to fail fast.
Solutions
- Wrap the consuming component in <ChainOfThought> ... </ChainOfThought>
- Move any component calling useChainOfThought inside the provider's children
- If the hook may run outside, guard with a nullable-context variant instead of the throwing one
- Check JSX ordering — the provider must be an ancestor, not a sibling
Example fix
// before
<ChainOfThoughtItem step={step} /> // thrown: no context
// after
<ChainOfThought isStreaming={isStreaming}>
<ChainOfThoughtItem step={step} />
</ChainOfThought> Defensive patterns
Strategy: type-guard
Type guard
function useChainOfThoughtSafe() {
return useContext(ChainOfThoughtContext); // returns null outside provider
}
// consumer:
const ctx = useChainOfThoughtSafe();
if (ctx === null) throw new Error('ChainOfThought components must be used within ChainOfThought'); Try / catch
try {
render(<ChainOfThoughtItem step={step} />);
} catch (e) {
if (e.message.includes('must be used within ChainOfThought')) {
console.error('JSX error: wrap the component in <ChainOfThought>');
}
} Prevention
- Always render subcomponents as children of <ChainOfThought>
- Add an ESLint/React rule or unit test that asserts the provider wraps consumers
- After refactors, re-check JSX ancestry of context-consuming components
- Keep hooks and their provider components in the same module to make the contract obvious
When it happens
Trigger: Calling useChainOfThought (or rendering a subcomponent like ChainOfThoughtItem that internally calls it) outside of a <ChainOfThought> ancestor — e.g. rendering thought items at the top level of a page.
Common situations: Refactoring moved a child component out of the ChainOfThought wrapper; conditional rendering mounts the child without the parent; storybook/test renders the hook's consumer standalone.
Related errors
- Reasoning components must be used within Reasoning
- DropDrawer components cannot be rendered outside the…
- Tool components must be used within Tool
- useSidebar must be used within a SidebarProvider.
- Backend installed but failed to refresh UI
AI-assisted analysis of janhq/jan@7205d770c1 (2026-09-17).
Data as JSON: /api/errors/263f15c11fa207a1.
Report an issue: GitHub.
Appendix: source
Thrown at web-app/src/components/ai-elements/chain-of-thought.tsx:57
// ── Context ────────────────────────────────────────────────────────────────
type ChainOfThoughtContextValue = {
isOpen: boolean
setIsOpen: (open: boolean) => void
isStreaming: boolean
duration: number | undefined
}
const MS_IN_S = 1000
const ChainOfThoughtContext = createContext<ChainOfThoughtContextValue | null>(
null
)
export const useChainOfThought = () => {
const context = useContext(ChainOfThoughtContext)
if (!context) {
throw new Error(
'ChainOfThought components must be used within ChainOfThought'
)
}
return context
}
// ── ChainOfThought (root) ──────────────────────────────────────────────────
export type ChainOfThoughtProps = ComponentProps<typeof Collapsible> & {
isStreaming?: boolean
/** When true the collapsible auto-collapses (e.g. text content appeared after this CoT group). */
shouldCollapse?: boolean
/** When true the collapsible is forced open and overrides auto-collapse (e.g. a tool is awaiting approval). */
forceOpen?: boolean
open?: boolean
defaultOpen?: boolean
onOpenChange?: (open: boolean) => void
/** Persisted trace duration (ms), used to seed the header when a restoredView on GitHub (pinned to 7205d770c1)