{"record":{"id":"92cd6b4ac530b1eb","repo":"mastra-ai/mastra","slug":"hookname-must-be-used-within-messagescrollerpro","errorCode":null,"errorMessage":"${hookName} must be used within MessageScrollerProvider.","messagePattern":"(.+?) must be used within MessageScrollerProvider\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/playground-ui/src/ds/components/MessageScroller/message-scroller-context.ts","lineNumber":54,"sourceCode":"export const DEFAULT_SCROLLABLE: MessageScrollerScrollable = { start: false, end: false };\nexport const DEFAULT_VISIBILITY: MessageScrollerVisibility = { currentAnchorId: undefined, visibleMessageIds: [] };\nexport const DEFAULT_SCROLL_EDGE_THRESHOLD = 8;\nexport const DEFAULT_SCROLL_MARGIN = 0;\nexport const DEFAULT_SCROLL_PREVIOUS_ITEM_PEEK = 64;\nexport const DEFAULT_REACH_START_THRESHOLD = 160;\n\n// Looser than the edge threshold, which only decides whether the scroll buttons\n// are active: how far from the end a reader may sit and still be carried along\n// by growing content.\nexport const AUTO_SCROLL_ATTACH_THRESHOLD = 160;\n\nexport const MessageScrollerActionsContext = React.createContext<MessageScrollerActionsContextValue | null>(null);\nexport const MessageScrollerScrollableContext = React.createContext<MessageScrollerScrollable | null>(null);\nexport const MessageScrollerVisibilityContext = React.createContext<MessageScrollerVisibility | null>(null);\n\nconst useRequiredContext = <TValue>(context: React.Context<TValue | null>, hookName: string) => {\n  const value = React.useContext(context);\n  if (!value) throw new Error(`${hookName} must be used within MessageScrollerProvider.`);\n  return value;\n};\n\nexport const useRequiredMessageScrollerActionsContext = (hookName: string) =>\n  useRequiredContext(MessageScrollerActionsContext, hookName);\n\nexport const useRequiredMessageScrollerScrollableContext = (hookName: string) =>\n  useRequiredContext(MessageScrollerScrollableContext, hookName);\n\nexport const useRequiredMessageScrollerVisibilityContext = (hookName: string) =>\n  useRequiredContext(MessageScrollerVisibilityContext, hookName);\n","sourceCodeStart":36,"sourceCodeEnd":66,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/playground-ui/src/ds/components/MessageScroller/message-scroller-context.ts#L36-L66","documentation":"The MessageScroller design-system component exposes its state through three React contexts (Actions, Scrollable, Visibility), each defaulting to null. Hooks that consume these contexts call useRequiredContext, which throws this error when no MessageScrollerProvider is mounted above the consuming component. It is an intentional programming-contract error: the scroller's actions, scroll state, and visibility APIs only exist once the provider initializes them.","triggerScenarios":"Calling useRequiredMessageScrollerActionsContext, useRequiredMessageScrollerScrollableContext, or useRequiredMessageScrollerVisibilityContext (each passing its own hookName) from a component that is not a descendant of <MessageScrollerProvider>, or rendering the consumer before/outside the provider due to conditional rendering, portals, or wrong import of the context file.","commonSituations":"Extracting a message-list subcomponent into its own file and forgetting to keep it inside the provider tree; rendering scroller toolbar buttons in a modal or portal that escapes the provider; splitting a page so the provider unmounts while children still reference the hooks; copy-pasting a hook into a storybook story without wrapping in the provider.","solutions":["Wrap the consuming component tree in <MessageScrollerProvider> so the three contexts are populated.","Move the component calling the hook inside the existing provider's children instead of rendering it as a sibling.","If using a portal, render the portal node inside the provider subtree (React context follows the React tree, not the DOM tree).","Verify you import the hooks from the public module and not re-implement context reads against the raw null-default contexts."],"exampleFix":"// before\nexport function ScrollToBottomButton() {\n  const actions = useRequiredMessageScrollerActionsContext('useMessageScrollerActions');\n  return <button onClick={actions.scrollToEnd}>Bottom</button>;\n}\n\n// after\nexport function ChatPanel() {\n  return (\n    <MessageScrollerProvider>\n      <ScrollToBottomButton />\n    </MessageScrollerProvider>\n  );\n}","handlingStrategy":"validation","validationCode":"function isInsideMessageScroller(el: HTMLElement | null): boolean {\n  return !!el?.closest('[data-message-scroller-root]');\n}\n// Render scroller-dependent UI only when the provider ancestor exists.","typeGuard":"const useMaybeMessageScrollerActions = () => {\n  const value = React.useContext(MessageScrollerActionsContext);\n  return value; // MessageScrollerActionsContextValue | null\n};\nconst hasActions = (v: unknown): v is MessageScrollerActionsContextValue => v != null;","tryCatchPattern":"try {\n  const actions = useRequiredMessageScrollerActionsContext('useMessageScrollerActions');\n  actions.scrollToEnd();\n} catch {\n  // not inside provider — render fallback/no-op\n}","preventionTips":["Always render scroller consumers as descendants of <MessageScrollerProvider>.","Remember React context follows the React tree — place portals inside the provider subtree.","Wrap reusable scroller UI in a component that includes the provider itself.","In stories/tests, mount the provider before exercising hooks."],"tags":["react","context","provider-missing","playground-ui"],"backgroundTag":"missing-react-context-provider","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}