Stirling-Tools/Stirling-PDF · error · Error
Unknown policy category: ${id}
Error message
Unknown policy category: ${id} What it means
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).
Source
Thrown at frontend/editor/src/proprietary/hooks/usePolicies.ts:140
if (attempt > 0) void refetchAppConfigRef.current();
};
void reconcile();
return () => {
cancelled = true;
if (timer) clearTimeout(timer);
};
}, []);
/**
* Enable a new policy from the wizard result: persist it to the backend (the
* source of truth), then create the backing folder holding its editable
* automation, and cache the result locally. Throws (surfacing in the wizard)
* if the category is unknown or the backend save fails.
*/
const enablePolicy = useCallback(
async (id: string, result: PolicyWizardResult) => {
const category = loadPolicyCatalog().categories.find((c) => c.id === id);
if (!category) throw new Error(`Unknown policy category: ${id}`);
// One policy per category, ever: reuse any existing backend record.
const existingBackendId =
loadPolicies()[id]?.backendId ??
(await findBackendId(id).catch(() => undefined));
const backendId = await persistPolicy(
toStoreRequest(id, category.label, result, true, existingBackendId),
);
const folder = await createPolicyFolderForAutomation(
category,
result.automation.id,
);
await updatePolicyFolderSettings(folder.id, result.folder);
updatePolicy(id, {
configured: true,
status: "active",
folderId: folder.id,
backendId,
fieldValues: result.fieldValues,View on GitHub (pinned to 9ef20dcab8)
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.
Example fix
// before
await enablePolicy(id, result); // throws on unknown id
// after
const category = loadPolicyCatalog().categories.find((c) => c.id === id);
if (!category) {
showToast(t('policies.categoryNotFound', { id }));
return;
}
await enablePolicy(id, result); Defensive patterns
Strategy: validation
Validate before calling
import { loadPolicyCatalog } from "@app/services/policyCatalog";
const known = new Set(loadPolicyCatalog().categories.map((c) => c.id));
if (!known.has(id)) {
// don't call enablePolicy; surface 'category unavailable'
} Type guard
export function isKnownPolicyCategory(id: string): boolean {
return loadPolicyCatalog().categories.some((c) => c.id === id);
} Try / catch
try {
await enablePolicy(id, result);
} catch (e) {
if (/Unknown policy category/i.test(e instanceof Error ? e.message : "")) {
showToast(t('policies.categoryNotFound', { id }));
} else throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- policy produced no output
- view.error || policy run ${view.status.toLowerCase()}
- Supabase is not configured
- Failed to submit signature
- Failed to decline
AI-assisted analysis of Stirling-Tools/Stirling-PDF@9ef20dcab8 (2026-08-13).
Data as JSON: /api/errors/96f10acfe3e12390.
Report an issue: GitHub.