{"record":{"id":"96f10acfe3e12390","repo":"Stirling-Tools/Stirling-PDF","slug":"unknown-policy-category-id","errorCode":null,"errorMessage":"Unknown policy category: ${id}","messagePattern":"Unknown policy category: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/editor/src/proprietary/hooks/usePolicies.ts","lineNumber":140,"sourceCode":"      if (attempt > 0) void refetchAppConfigRef.current();\n    };\n    void reconcile();\n    return () => {\n      cancelled = true;\n      if (timer) clearTimeout(timer);\n    };\n  }, []);\n\n  /**\n   * Enable a new policy from the wizard result: persist it to the backend (the\n   * source of truth), then create the backing folder holding its editable\n   * automation, and cache the result locally. Throws (surfacing in the wizard)\n   * if the category is unknown or the backend save fails.\n   */\n  const enablePolicy = useCallback(\n    async (id: string, result: PolicyWizardResult) => {\n      const category = loadPolicyCatalog().categories.find((c) => c.id === id);\n      if (!category) throw new Error(`Unknown policy category: ${id}`);\n      // One policy per category, ever: reuse any existing backend record.\n      const existingBackendId =\n        loadPolicies()[id]?.backendId ??\n        (await findBackendId(id).catch(() => undefined));\n      const backendId = await persistPolicy(\n        toStoreRequest(id, category.label, result, true, existingBackendId),\n      );\n      const folder = await createPolicyFolderForAutomation(\n        category,\n        result.automation.id,\n      );\n      await updatePolicyFolderSettings(folder.id, result.folder);\n      updatePolicy(id, {\n        configured: true,\n        status: \"active\",\n        folderId: folder.id,\n        backendId,\n        fieldValues: result.fieldValues,","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/Stirling-Tools/Stirling-PDF/blob/9ef20dcab80b85041912f045e17a6aea1d08f969/frontend/editor/src/proprietary/hooks/usePolicies.ts#L122-L158","documentation":"Thrown by enablePolicy() when the passed policy id doesn't match any category in loadPolicyCatalog().categories. The catalog is the authoritative list of policy categories; the wizard path can only enable a known category because the backend store-request needs category.label. This guard prevents persisting a policy for a category the rest of the system doesn't know about (no label, no folder template, no UI row).","triggerScenarios":"enablePolicy('someId', result) called with a typo'd or stale id; the catalog was loaded from localStorage/IndexedDB that predates a catalog schema change so the id no longer exists; a wizard deep-link or URL parameter references a removed category; the catalog failed to load (empty array) and every lookup misses.","commonSituations":"A user bookmarks an old wizard URL after a release that renamed/removed a category; local catalog cache is corrupt or from an older app version; developer passes the wrong id when wiring a new entry point to enablePolicy.","solutions":["Confirm the id matches a category.id in loadPolicyCatalog().categories before calling enablePolicy (validate at the call site).","Clear stale policy catalog cache (localStorage / IndexedDB) if it predates the current schema, then reload.","If the category was intentionally removed, update the caller to stop referencing it or remap it to the new id.","Add a type/const for allowed category ids so typos surface at compile time."],"exampleFix":"// before\nawait enablePolicy(id, result); // throws on unknown id\n\n// after\nconst category = loadPolicyCatalog().categories.find((c) => c.id === id);\nif (!category) {\n  showToast(t('policies.categoryNotFound', { id }));\n  return;\n}\nawait enablePolicy(id, result);","handlingStrategy":"validation","validationCode":"import { loadPolicyCatalog } from \"@app/services/policyCatalog\";\n\nconst known = new Set(loadPolicyCatalog().categories.map((c) => c.id));\nif (!known.has(id)) {\n  // don't call enablePolicy; surface 'category unavailable'\n}","typeGuard":"export function isKnownPolicyCategory(id: string): boolean {\n  return loadPolicyCatalog().categories.some((c) => c.id === id);\n}","tryCatchPattern":"try {\n  await enablePolicy(id, result);\n} catch (e) {\n  if (/Unknown policy category/i.test(e instanceof Error ? e.message : \"\")) {\n    showToast(t('policies.categoryNotFound', { id }));\n  } else throw e;\n}","preventionTips":["Derive category ids from a single typed const union so typos fail at compile time.","Reconcile stored policy rows against the catalog on load and mark unmatched ones rather than letting users act on them.","Clear stale catalog cache after schema migrations."],"tags":["policies","validation","catalog","proprietary","typescript"],"backgroundTag":null,"analyzedSha":"9ef20dcab80b85041912f045e17a6aea1d08f969","analyzedAt":"2026-08-13T22:11:39.827Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}