{"record":{"id":"a33799672ee1f895","repo":"MemPalace/mempalace","slug":"write-routing-scope-must-be-hooks-or-cli","errorCode":null,"errorMessage":"write routing scope must be 'hooks' or 'cli'","messagePattern":"write routing scope must be 'hooks' or 'cli'","errorType":"validation","errorClass":"WriteRoutingError","httpStatus":null,"severity":"error","filePath":"mempalace/config.py","lineNumber":1011,"sourceCode":"        2. global environment variable;\n        3. legacy hook environment variable;\n        4. scope-specific config value;\n        5. global config value;\n        6. legacy hook config value;\n        7. ``direct``.\n\n        This foundation does not change current hook or CLI behavior. The\n        policy-aware consumers are introduced by follow-up PRs.\n        \"\"\"\n\n        normalized_scope = str(scope).strip().lower()\n        env_names = {\n            \"hooks\": \"MEMPALACE_HOOK_WRITE_ROUTING\",\n            \"cli\": \"MEMPALACE_CLI_WRITE_ROUTING\",\n        }\n\n        if normalized_scope not in env_names:\n            raise WriteRoutingError(\"write routing scope must be 'hooks' or 'cli'\")\n\n        routing_config = self._file_config.get(\"write_routing\", {})\n        if routing_config is None:\n            routing_config = {}\n\n        if not isinstance(routing_config, dict):\n            raise WriteRoutingError(\"config write_routing must be an object\")\n\n        candidates = [\n            RoutingPolicyCandidate(\n                env_names[normalized_scope],\n                os.environ.get(env_names[normalized_scope]),\n            ),\n            RoutingPolicyCandidate(\n                \"MEMPALACE_WRITE_ROUTING\",\n                os.environ.get(\"MEMPALACE_WRITE_ROUTING\"),\n            ),\n        ]","sourceCodeStart":993,"sourceCodeEnd":1029,"githubUrl":"https://github.com/MemPalace/mempalace/blob/06cb6987f02610784fefbad4b2bd5d026d164ba6/mempalace/config.py#L993-L1029","documentation":"WriteRoutingError raised in MempalaceConfig's write-routing resolution (mempalace/config.py:1011) when the scope argument passed to the policy resolver is not 'hooks' or 'cli' after strip+lower normalization. The scope selects which environment variable (MEMPALACE_HOOK_WRITE_ROUTING vs MEMPALACE_CLI_WRITE_ROUTING) anchors the candidate chain, so an unknown scope has no defined semantics and is rejected before any config is read.","triggerScenarios":"Calling the internal write-routing resolution method with scope=\"mcp\", scope=\"hook\" (singular), or scope=\"\" ; passing a non-string scope like None or 1 that str()-ifies to something outside {hooks, cli}; a new call site added during development that uses an unsanitized scope string from user input.","commonSituations":"Extending MemPalace with a new entry point (e.g. an MCP tool path) and passing that label as the scope; refactoring that renames scope constants; dynamic scope built from a config key or CLI arg that is misspelled.","solutions":["Pass exactly 'hooks' or 'cli' (case-insensitive; surrounding whitespace is tolerated) to the resolution method.","If adding a genuinely new write path, extend the env_names mapping in config.py rather than passing an ad-hoc scope.","Search your call sites for the scope value being interpolated (rg for the method name) and fix the producer of the bad value."],"exampleFix":"# before\npolicy = config.write_routing_policy(\"mcp\")\n\n# after\npolicy = config.write_routing_policy(\"cli\")","handlingStrategy":"type-guard","validationCode":"scope = scope.strip().lower()\nassert scope in {\"hooks\", \"cli\"}, f\"bad scope: {scope!r}\"","typeGuard":"from typing import Literal\nWriteScope = Literal[\"hooks\", \"cli\"]\n\ndef is_write_scope(value: object) -> bool:\n    return isinstance(value, str) and value.strip().lower() in {\"hooks\", \"cli\"}","tryCatchPattern":"from mempalace.config import WriteRoutingError\n\ntry:\n    policy = config.write_routing_policy(scope)\nexcept WriteRoutingError as exc:\n    # surface caller-facing config error; do not retry\n    raise","preventionTips":["Type scope parameters as Literal['hooks','cli'] so type checkers catch bad call sites.","Never build the scope string from unvalidated user input.","Keep a single constant per entry point instead of ad-hoc strings."],"tags":["config","write-routing","validation"],"backgroundTag":null,"analyzedSha":"06cb6987f02610784fefbad4b2bd5d026d164ba6","analyzedAt":"2026-08-15T03:03:36.213Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}