mastra-ai/mastra · error · Error
Plan compound components must be rendered inside <Plan>.
Error message
Plan compound components must be rendered inside <Plan>.
What it means
The Plan component is a compound component: <Plan> provides PlanContext (collapsedHeight, isExpanded, isClipped, setClipped, toggleExpanded) and its subcomponents (e.g., Plan.Body/Content) consume it via usePlanContext. If a compound child renders without an ancestor <Plan>, the context is null and this error is thrown, since expansion/clipping state cannot be resolved.
Source
Thrown at packages/playground-ui/src/ds/components/ai/plan/plan.tsx:30
const DEFAULT_COLLAPSED_HEIGHT = 220;
interface PlanContextValue {
collapsedHeight: number;
isExpanded: boolean;
/** Whether the rendered content overflows the collapsed height (measured, not estimated). */
isClipped: boolean;
setClipped: (clipped: boolean) => void;
toggleExpanded: () => void;
}
const PlanContext = createContext<PlanContextValue | null>(null);
const usePlanContext = () => {
const context = useContext(PlanContext);
if (!context) {
throw new Error('Plan compound components must be rendered inside <Plan>.');
}
return context;
};
export interface PlanProps extends ComponentProps<'div'> {
collapsedHeight?: number;
}
export function Plan({ children, collapsedHeight = DEFAULT_COLLAPSED_HEIGHT, className, ...props }: PlanProps) {
const [isExpanded, setIsExpanded] = useState(false);
const [isClipped, setClipped] = useState(false);
const toggleExpanded = () => {
setIsExpanded(current => !current);
};
const contextValue = {View on GitHub (pinned to 75dd419e61)
Solutions
- Wrap all Plan compound children inside <Plan>.
- Restore the <Plan> wrapper if a refactor removed it while keeping the child components.
- Pass expansion state via props instead of using Plan compounds if you need them in a different tree.
- Render children through Plan's render/children API so the context is guaranteed present.
Example fix
// before
<Plan.Content isExpanded={isExpanded} /> {/* throws */}
// after
<Plan title="Agent plan">
<Plan.Content />
</Plan> Defensive patterns
Strategy: validation
Validate before calling
const plan = useContext(PlanContext);
if (!plan) {
// render a standalone non-compound view instead of Plan subcomponents
} Type guard
const hasPlanContext = (v: PlanContextValue | null): v is PlanContextValue => v != null;
Try / catch
try {
const plan = usePlanContext();
plan.toggleExpanded();
} catch {
// outside <Plan>: use local expansion state
} Prevention
- Treat Plan subcomponents as private to the <Plan> wrapper; never import them standalone.
- When refactoring plan layouts, keep the <Plan> root as the outermost element.
- Provide expansion state via props for usage outside the compound.
- Verify stories render the full <Plan> composition.
When it happens
Trigger: Rendering Plan subcomponents (which call usePlanContext) outside a <Plan> parent; composing Plan parts into a custom layout where the <Plan> wrapper was removed; conditionally rendering <Plan> but always rendering its children.
Common situations: Refactoring an AI plan card into a custom grid and dropping the wrapper; reusing a Plan.ClampedContent section in another view; storybook stories rendering subcomponents alone.
Related errors
- ToolCall compounds must be rendered within ToolCall
- Comment compounds must be rendered within Comment
- ${componentName} must be used within EnvironmentVariablesEdi
- useJSONSchemaForm must be used within a JSONSchemaForm.Root
- useJSONSchemaFormField must be used within a JSONSchemaForm.
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/88773c6ddb75109c.
Report an issue: GitHub.