{"record":{"id":"6b6bf200218991a5","repo":"iflytek/astron-agent","slug":"useslot-must-be-used-within-slotprovider","errorCode":null,"errorMessage":"useSlot must be used within SlotProvider","messagePattern":"useSlot must be used within SlotProvider","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"console/frontend/src/pages/release-management/detail-list-page/index.tsx","lineNumber":31,"sourceCode":"import errorIcon from '@/assets/imgs/sparkImg/errorIcon.svg';\nimport workflowIcon from '@/assets/imgs/release/workflowIcon.svg';\n\nimport styles from './index.module.scss';\nimport { useTranslation } from 'react-i18next';\n\n// 创建插槽上下文\ninterface SlotContextType {\n  registerSlotContent: (content: React.ReactNode) => void;\n  unregisterSlotContent: () => void;\n}\n\nconst SlotContext = createContext<SlotContextType | null>(null);\n\n// 导出hook供子组件使用\nexport const useSlot = () => {\n  const context = useContext(SlotContext);\n  if (!context) {\n    throw new Error('useSlot must be used within SlotProvider');\n  }\n  return context;\n};\n\nconst DetailListPage = () => {\n  const navigate = useNavigate();\n  const location = useLocation();\n  const { botId } = useParams();\n  const [record, setRecord] = useState(location.state?.record);\n  const [slotContent, setSlotContent] = useState<React.ReactNode>(null);\n  const { t } = useTranslation();\n\n  // 插槽内容管理方法\n  const registerSlotContent = useCallback((content: React.ReactNode) => {\n    setSlotContent(content);\n  }, []);\n\n  const unregisterSlotContent = useCallback(() => {","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/console/frontend/src/pages/release-management/detail-list-page/index.tsx#L13-L49","documentation":"`useSlot` reads a context created by SlotProvider (used for release-management detail pages). The context defaults to null and the hook throws 'useSlot must be used within SlotProvider' when null. It guarantees slot data (dynamic page slots) is always available to consumers.","triggerScenarios":"Rendering a slot-consuming child outside the SlotProvider in release-management detail-list-page, rendering the page fragment in a different route, or conditionally rendering the consumer while the provider is skipped.","commonSituations":"Extracting a child component to another page without moving the provider; test/story render missing the wrapper; provider unmounted early by a conditional (e.g. loading state bypasses the provider).","solutions":["Wrap the consuming subtree with <SlotProvider> (as done in DetailListPage).","Ensure the provider is rendered on every route where slot children appear.","In tests, wrap the component under test with SlotProvider.","If optional use is intended, export a non-throwing hook returning context as possibly-null."],"exampleFix":"// before\nreturn <SlotConsumer />; // throws\n\n// after\n<SlotProvider value={slotValue}>\n  <SlotConsumer />\n</SlotProvider>","handlingStrategy":"try-catch","validationCode":"const useSlotSafe = (): SlotContextType | null => useContext(SlotContext);","typeGuard":"const useHasSlot = (): boolean => useContext(SlotContext) !== null;","tryCatchPattern":"let slot: SlotContextType;\ntry {\n  slot = useSlot();\n} catch {\n  return <FallbackNoSlots />;\n}","preventionTips":["Render SlotProvider at the release-detail page root on all related routes.","Wrap Storybook/test renders with SlotProvider.","Avoid conditional rendering branches that mount consumers but skip the provider.","Prefer passing slot data via props when a component may be reused outside the provider."],"tags":["react","context","provider"],"backgroundTag":"missing-required-argument","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}