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

  1. Wrap the consuming component in <ChainOfThought> ... </ChainOfThought>
  2. Move any component calling useChainOfThought inside the provider's children
  3. If the hook may run outside, guard with a nullable-context variant instead of the throwing one
  4. 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

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


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 restored

View on GitHub (pinned to 7205d770c1)