janhq/jan · error · Error

Reasoning components must be used within Reasoning

Error message

Reasoning components must be used within Reasoning

What it means

useReasoning is a React context hook that reads ReasoningContext, populated only by the Reasoning provider component. When called outside a <Reasoning> ancestor the context is null and the hook throws this Error to surface the misuse immediately.

Solutions

  1. Wrap the consuming components in <Reasoning> ... </Reasoning>
  2. Ensure ReasoningTrigger and ReasoningContent are children of Reasoning, not siblings
  3. Guard with a non-throwing context hook if usage outside is legitimate
  4. Verify conditional rendering never mounts the consumer without the provider

Example fix

// before
<ReasoningTrigger />
<ReasoningContent /> // thrown: no context
// after
<Reasoning isStreaming={isStreaming} duration={duration}>
  <ReasoningTrigger />
  <ReasoningContent />
</Reasoning>
Defensive patterns

Strategy: type-guard

Type guard

function useReasoningSafe() {
  return useContext(ReasoningContext); // null outside provider
}
// consumer:
const ctx = useReasoningSafe();
if (ctx === null) throw new Error('Reasoning components must be used within Reasoning');

Try / catch

try {
  render(<ReasoningContent />);
} catch (e) {
  if (e.message.includes('must be used within Reasoning')) {
    console.error('JSX error: wrap the component in <Reasoning>');
  }
}

Prevention

When it happens

Trigger: Calling useReasoning (or rendering ReasoningTrigger/ReasoningContent outside the wrapper) without a <Reasoning> ancestor in the component tree.

Common situations: Moving the reasoning toggle or content block into a different parent during refactor; rendering reasoning parts in a message list without the Reasoning wrapper; isolated test/storybook mounting.

Related errors


AI-assisted analysis of janhq/jan@7205d770c1 (2026-09-17). Data as JSON: /api/errors/d8561337db610cab. Report an issue: GitHub.

Appendix: source

Thrown at web-app/src/components/ai-elements/reasoning.tsx:34

  useMemo,
  useState,
} from 'react'
import { Streamdown } from 'streamdown'
import { Shimmer } from './shimmer'

type ReasoningContextValue = {
  isStreaming: boolean
  isOpen: boolean
  setIsOpen: (open: boolean) => void
  duration: number | undefined
}

const ReasoningContext = createContext<ReasoningContextValue | null>(null)

export const useReasoning = () => {
  const context = useContext(ReasoningContext)
  if (!context) {
    throw new Error('Reasoning components must be used within Reasoning')
  }
  return context
}

export type ReasoningProps = ComponentProps<typeof Collapsible> & {
  isStreaming?: boolean
  open?: boolean
  defaultOpen?: boolean
  onOpenChange?: (open: boolean) => void
  duration?: number
}

const MS_IN_S = 1000

export const Reasoning = memo(
  ({
    className,
    isStreaming = false,

View on GitHub (pinned to 7205d770c1)