{"record":{"id":"e89162e5cf15bc41","repo":"nocobase/nocobase","slug":"usetools-must-be-used-within-a-toolsprovider","errorCode":null,"errorMessage":"useTools must be used within a ToolsProvider","messagePattern":"useTools must be used within a ToolsProvider","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/client-v2/src/ai/tools-manager/hooks/index.ts","lineNumber":16,"sourceCode":"/**\n * This file is part of the NocoBase (R) project.\n * Copyright (c) 2020-2024 NocoBase Co., Ltd.\n * Authors: NocoBase Team.\n *\n * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.\n * For more information, please refer to: https://www.nocobase.com/agreement.\n */\n\nimport { useContext } from 'react';\nimport { ToolsContext } from './context';\n\nexport const useTools = () => {\n  const ctx = useContext(ToolsContext);\n  if (!ctx) {\n    throw new Error('useTools must be used within a ToolsProvider');\n  }\n  return ctx;\n};\n\nexport * from './context';\nexport * from './provider';\n","sourceCodeStart":1,"sourceCodeEnd":23,"githubUrl":"https://github.com/nocobase/nocobase/blob/fa42722fefe44265490dff2c27d79e2882bce4fa/packages/core/client-v2/src/ai/tools-manager/hooks/index.ts#L1-L23","documentation":"useTools is a React hook that reads ToolsContext via useContext; it throws when the context value is null/undefined, i.e. when no <ToolsProvider> ancestor has rendered. The library deliberately throws instead of returning a null context so consumers fail fast at the point of misuse rather than crashing later on a missing property.","triggerScenarios":"Calling useTools() from a component rendered outside a <ToolsProvider>, or from module-level/non-React code with no React tree ancestor.","commonSituations":"A new tool-consuming component is mounted in a route/portal that forgot the provider; a component was moved out of the provider subtree during refactoring; a hook is called in a test with render(<Component/>) without wrapping render in the provider.","solutions":["Wrap the consuming component tree in <ToolsProvider> (usually at an app/layout level above where useTools is called).","Check that the component using useTools is actually a descendant of ToolsProvider, including portals and lazy-loaded subtrees.","In tests, wrap the component with ToolsProvider inside render() (or create a renderWithProviders helper)."],"exampleFix":"// before\nexport default function Panel() {\n  const tools = useTools(); // throws\n  return null;\n}\n// after\nexport default function App() {\n  return (\n    <ToolsProvider>\n      <Panel />\n    </ToolsProvider>\n  );\n}","handlingStrategy":"try-catch","validationCode":"const canUseTools = typeof ToolsContext !== 'undefined'; // and ensure provider is mounted above\n// in React DevTools or via a check component:\n// const ctx = useContext(ToolsContext); if (!ctx) return <ToolsProvider><Child/></ToolsProvider>;","typeGuard":"function hasToolsContext(ctx: ReturnType<typeof useContext<typeof ToolsContext>> | null): ctx is NonNullable<ReturnType<typeof useContext<typeof ToolsContext>>> {\n  return ctx != null;\n}","tryCatchPattern":"let tools: ToolsContextValue;\ntry {\n  tools = useTools();\n} catch {\n  return <ToolsProvider><RealComponent /></ToolsProvider>;\n}","preventionTips":["Mount ToolsProvider once at the app root so every route is covered.","Create a renderWithProviders test helper that always includes ToolsProvider.","Never call useTools from module scope or plain (non-component) functions.","Prefer exporting a wrapper component over raw hooks so the provider travels with the feature."],"tags":["react","context","provider-missing"],"backgroundTag":"react-hook-outside-provider","analyzedSha":"fa42722fefe44265490dff2c27d79e2882bce4fa","analyzedAt":"2026-09-01T00:54:31.202Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}