{"record":{"id":"c73d77dca4f1a042","repo":"mastra-ai/mastra","slug":"factory-project-is-required","errorCode":null,"errorMessage":"Factory project is required","messagePattern":"Factory project is required","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mastracode/factory-ui/src/hooks/useFactoryAttention.ts","lineNumber":56,"sourceCode":"    : skipToken;\n  return useInfiniteQuery({\n    queryKey: [...queryKeys.factoryAttention(factoryProjectId, view, 25), 'history', search],\n    queryFn,\n    initialPageParam,\n    getNextPageParam: lastPage => lastPage.nextCursor,\n    staleTime: 2_000,\n  });\n}\n\nexport function useFactoryAttentionReceiptAction(\n  factoryProjectId: string | undefined,\n  action: FactoryAttentionReceiptAction,\n) {\n  const { baseUrl } = useApiConfig();\n  const queryClient = useQueryClient();\n  return useMutation({\n    mutationFn: (item: FactoryAttentionItem) => {\n      if (!factoryProjectId) throw new Error('Factory project is required');\n      return updateFactoryAttentionReceipt(baseUrl, factoryProjectId, item, action);\n    },\n    onSuccess: async () => {\n      await queryClient.invalidateQueries({ queryKey: queryKeys.factoryAttentionRoot(factoryProjectId) });\n    },\n  });\n}\n\nexport function useMarkAllFactoryAttentionRead(factoryProjectId: string | undefined) {\n  const { baseUrl } = useApiConfig();\n  const queryClient = useQueryClient();\n  return useMutation({\n    mutationFn: () => {\n      if (!factoryProjectId) throw new Error('Factory project is required');\n      return markAllFactoryAttentionRead(baseUrl, factoryProjectId);\n    },\n    onSuccess: async () => {\n      await queryClient.invalidateQueries({ queryKey: queryKeys.factoryAttentionRoot(factoryProjectId) });","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory-ui/src/hooks/useFactoryAttention.ts#L38-L74","documentation":"The attention receipt mutation (read/archive/restore) requires a `factoryProjectId` to know which project's attention items to update. If the hook is invoked without one, it throws immediately in the mutation function rather than issuing a malformed API call.","triggerScenarios":"`useFactoryAttentionReceiptAction` is used on a page/component where the `factoryProjectId` param is undefined/null, and the user triggers readItem, archiveItem, or restoreItem.","commonSituations":"Route param not yet resolved when the component renders (deep-link or refresh before data loads); component used outside a project context; project selection cleared but action buttons still enabled.","solutions":["Ensure `factoryProjectId` is resolved (from route params/query) before rendering the action controls, or disable them until it exists","Pass the project ID explicitly when instantiating the hook","Guard the mutation call site with the same check and surface a user-facing message","If the ID comes from a route, wait for the loader/param before mounting the component"],"exampleFix":"// before\nconst action = useFactoryAttentionReceiptAction(baseUrl, projectId, 'archive');\n// after (caller-side guard)\nconst action = useFactoryAttentionReceiptAction(baseUrl, projectId, 'archive');\n<button\n  disabled={!projectId || action.isPending}\n  onClick={() => projectId && action.mutate(item)}\n/>","handlingStrategy":"validation","validationCode":"if (!factoryProjectId) {\n  // do not mount/enable the mutation or the action buttons\n  return <DisabledTooltip message=\"Select a Factory project first\" />;\n}","typeGuard":"function hasProjectId(id: string | null | undefined): id is string {\n  return typeof id === 'string' && id.length > 0;\n}","tryCatchPattern":"try {\n  await updateFactoryAttentionReceipt(baseUrl, factoryProjectId, item, action);\n} catch (e) {\n  if (e instanceof Error && e.message === 'Factory project is required') {\n    showToast('Select a Factory project before this action');\n  } else throw e;\n}","preventionTips":["Resolve factoryProjectId from route/loader before rendering action components","Disable read/archive/restore buttons until a project is selected","Type the hook param as required (string, not optional) when a project is guaranteed","Keep project selection persisted so refreshes don't clear it mid-action"],"tags":["validation","react-query","missing-param","factory-ui"],"backgroundTag":"missing-required-parameter","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}