{"record":{"id":"77623b49d93affdc","repo":"can1357/oh-my-pi","slug":"unknown-rule-rulename-available-availablest","errorCode":null,"errorMessage":"Unknown rule: ${ruleName}\nAvailable: ${availableStr}","messagePattern":"Unknown rule: (.+?)\nAvailable: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/internal-urls/rule-protocol.ts","lineNumber":26,"sourceCode":"import type { InternalResource, InternalUrl, ProtocolHandler, UrlCompletion } from \"./types\";\n\nexport class RuleProtocolHandler implements ProtocolHandler {\n\treadonly scheme = \"rule\";\n\treadonly immutable = true;\n\n\tasync resolve(url: InternalUrl): Promise<InternalResource> {\n\t\tconst rules = getActiveRules();\n\n\t\tconst ruleName = url.rawHost || url.hostname;\n\t\tif (!ruleName) {\n\t\t\tthrow new Error(\"rule:// URL requires a rule name: rule://<name>\");\n\t\t}\n\n\t\tconst rule = rules.find(r => r.name === ruleName);\n\t\tif (!rule) {\n\t\t\tconst available = rules.map(r => r.name);\n\t\t\tconst availableStr = available.length > 0 ? available.join(\", \") : \"none\";\n\t\t\tthrow new Error(`Unknown rule: ${ruleName}\\nAvailable: ${availableStr}`);\n\t\t}\n\n\t\treturn {\n\t\t\turl: url.href,\n\t\t\tcontent: rule.content,\n\t\t\tcontentType: \"text/markdown\",\n\t\t\tsize: Buffer.byteLength(rule.content, \"utf-8\"),\n\t\t\tsourcePath: rule.path,\n\t\t\tnotes: [],\n\t\t};\n\t}\n\n\tasync complete(): Promise<UrlCompletion[]> {\n\t\treturn getActiveRules().map(rule => ({\n\t\t\tvalue: rule.name,\n\t\t\t...(rule.description ? { description: rule.description } : {}),\n\t\t}));\n\t}","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/rule-protocol.ts#L8-L44","documentation":"The rule:// protocol handler resolves internal URLs of the form rule://<name> to the markdown content of an active capability rule. Before resolving, it looks up the rule name in the list returned by getActiveRules(); if no rule with that exact name is registered, it throws this error listing all currently available rule names (or \"none\" if no rules are active). It is a lookup-failure error meaning the referenced rule does not exist or is not active in the current session.","triggerScenarios":"Calling RuleProtocolHandler.resolve(new URL('rule://some-name')) (or resolving a rule:// internal URL through the URL registry) where 'some-name' does not exactly match the name of any rule returned by getActiveRules(): a typo in the rule name, a rule that was renamed or removed, a rule defined in a directory not loaded as active, or referencing a rule:// URL before rules are registered.","commonSituations":"Hand-written agent prompts or configs referencing rule:// URLs with stale names after a rules refactor; case or hyphen/underscore mismatches between the URL host and the rule name; rules loaded from a different working directory than the one active at resolve time; relying on a rule that only exists when certain capabilities/directories are enabled.","solutions":["Read the 'Available:' line in the error message and use one of the listed rule names exactly (they are case- and separator-sensitive).","Call getActiveRules() (or the rule:// completion endpoint) in your session to enumerate valid rule names and verify the one you reference exists.","If the rule should exist, check that it is defined in a location that is actually loaded/active (correct project directory, rules enabled in settings) before resolving the URL.","If the rule was renamed or deleted, update all prompt/config references to the new rule://<name> URL."],"exampleFix":"// before\nawait resolveUrl(new URL(\"rule://code-style\"));\n// after (verify the exact active rule name first)\nconst names = getActiveRules().map(r => r.name); // e.g. [\"code_style\"]\nif (names.includes(\"code_style\")) {\n  await resolveUrl(new URL(\"rule://code_style\"));\n}","handlingStrategy":"validation","validationCode":"import { getActiveRules } from \"../capability/rule\";\n\nfunction canResolveRule(name: string): boolean {\n  return getActiveRules().some(r => r.name === name);\n}\n\nconst ruleName = url.hostname;\nif (!canResolveRule(ruleName)) {\n  const available = getActiveRules().map(r => r.name);\n  throw new Error(`Rule \"${ruleName}\" not active. Available: ${available.join(\", \") || \"none\"}`);\n}","typeGuard":"function findActiveRule(name: string): Rule | null {\n  return getActiveRules().find(r => r.name === name) ?? null;\n}\n// use: const rule = findActiveRule(name); if (rule === null) { /* handle */ }","tryCatchPattern":"try {\n  const resource = await ruleHandler.resolve(url);\n  return resource;\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"Unknown rule:\")) {\n    const available = err.message.split(\"Available:\")[1]?.trim() ?? \"none\";\n    return fallbackResource(`Rule not found. Active rules: ${available}`);\n  }\n  throw err;\n}","preventionTips":["Generate rule:// URLs from the completion API or getActiveRules() instead of hardcoding names.","Treat rule names as case- and separator-sensitive identifiers; copy them verbatim.","After any rules rename/refactor, grep prompts and configs for rule:// references.","Resolve rule URLs only after rules are loaded for the active project directory."],"tags":["internal-urls","lookup-failed","config"],"backgroundTag":"resource-not-found","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}