{"record":{"id":"9b2f90ec786164f6","repo":"paperclipai/paperclip","slug":"select-a-skill-first-9b2f90","errorCode":null,"errorMessage":"Select a skill first.","messagePattern":"Select a skill first\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ui/src/pages/CompanySkills.production.tsx","lineNumber":4459,"sourceCode":"      setEditMode(false);\n      pushToast({\n        tone: \"success\",\n        title: \"Skill saved\",\n        body: result.path,\n      });\n    },\n    onError: (error) => {\n      pushToast({\n        tone: \"error\",\n        title: \"Save failed\",\n        body: error instanceof Error ? error.message : \"Failed to save skill file.\",\n      });\n    },\n  });\n\n  const toggleStar = useMutation({\n    mutationFn: () => {\n      if (!activeDetail) throw new Error(\"Select a skill first.\");\n      return activeDetail.starredByCurrentActor\n        ? companySkillsApi.unstar(selectedCompanyId!, activeDetail.id)\n        : companySkillsApi.star(selectedCompanyId!, activeDetail.id);\n    },\n    onSuccess: async () => {\n      if (!activeDetail) return;\n      await Promise.all([\n        queryClient.invalidateQueries({ queryKey: queryKeys.companySkills.list(selectedCompanyId!) }),\n        queryClient.invalidateQueries({ queryKey: queryKeys.companySkills.detail(selectedCompanyId!, activeDetail.id) }),\n      ]);\n    },\n    onError: (error) => {\n      pushToast({\n        tone: \"error\",\n        title: \"Star failed\",\n        body: error instanceof Error ? error.message : \"Failed to update star.\",\n      });\n    },","sourceCodeStart":4441,"sourceCodeEnd":4477,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/ui/src/pages/CompanySkills.production.tsx#L4441-L4477","documentation":"The toggleStar mutation requires an active skill detail (activeDetail) to know which skill to star/unstar. If the detail pane has no selection, it throws this error instead of calling companySkillsApi.star/unstar. It is a client-side precondition guard against an ambiguous star toggle.","triggerScenarios":"Invoking toggleStar.mutate() while activeDetail is null/undefined — e.g. clicking the star control before a skill's detail has loaded or after the selection was cleared.","commonSituations":"Rendering the star toggle for a not-yet-loaded detail; a filter/search change cleared the active detail while the control stayed clickable; a race where the detail query is still fetching when the user clicks.","solutions":["Select a skill so the detail pane is populated, then toggle the star.","Disable the star button when !activeDetail instead of allowing the mutation to run.","Wire the toggle to an explicit skill id from the list row rather than shared activeDetail state.","If activeDetail should always exist on this screen, investigate why detail selection was cleared (navigation, query error)."],"exampleFix":"// before\nmutationFn: () => {\n  if (!activeDetail) throw new Error(\"Select a skill first.\");\n  return activeDetail.starredByCurrentActor\n    ? companySkillsApi.unstar(selectedCompanyId!, activeDetail.id)\n    : companySkillsApi.star(selectedCompanyId!, activeDetail.id);\n},\n// after\nmutationFn: () => {\n  if (!activeDetail) throw new Error(\"Select a skill first.\");\n  return activeDetail.starredByCurrentActor\n    ? companySkillsApi.unstar(selectedCompanyId!, activeDetail.id)\n    : companySkillsApi.star(selectedCompanyId!, activeDetail.id);\n},\n// plus: <button disabled={!activeDetail} onClick={() => toggleStar.mutate()}>...</button>","handlingStrategy":"validation","validationCode":"if (!activeDetail) {\n  toast.info(\"Select a skill first.\");\n  return;\n}","typeGuard":"const hasDetail = (d: typeof activeDetail): d is NonNullable<typeof activeDetail> => d != null;","tryCatchPattern":"try {\n  await toggleStar.mutateAsync();\n} catch (e) {\n  if (e.message === \"Select a skill first.\") toast.info(e.message);\n  else toast.error(\"Could not update star\");\n}","preventionTips":["Disable the star toggle whenever activeDetail is null.","Bind star actions to the list row's skill id instead of shared detail state.","Clear pending UI affordances when the active detail selection changes."],"tags":["ui","missing-selection","star","guard"],"backgroundTag":"missing-required-argument","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}