{"record":{"id":"d953887af10242e0","repo":"MemPalace/mempalace","slug":"config-hooks-must-be-an-object","errorCode":null,"errorMessage":"config hooks must be an object","messagePattern":"config hooks must be an object","errorType":"validation","errorClass":"WriteRoutingError","httpStatus":null,"severity":"error","filePath":"mempalace/config.py","lineNumber":1059,"sourceCode":"            [\n                RoutingPolicyCandidate(\n                    f\"config write_routing.{normalized_scope}\",\n                    routing_config.get(normalized_scope),\n                ),\n                RoutingPolicyCandidate(\n                    \"config write_routing.default\",\n                    routing_config.get(\"default\"),\n                ),\n            ]\n        )\n\n        if normalized_scope == \"hooks\":\n            hooks_config = self._file_config.get(\"hooks\", {})\n            if hooks_config is None:\n                hooks_config = {}\n\n            if not isinstance(hooks_config, dict):\n                raise WriteRoutingError(\"config hooks must be an object\")\n\n            candidates.append(\n                RoutingPolicyCandidate(\n                    \"config hooks.daemon (legacy)\",\n                    hooks_config.get(\"daemon\"),\n                    legacy_boolean=True,\n                )\n            )\n\n        return resolve_write_routing_policy(candidates)\n\n    @property\n    def hook_write_routing(self) -> WriteRoutingPolicy:\n        \"\"\"Resolved future routing policy for hook-triggered writes.\"\"\"\n\n        return self.resolve_write_routing(\"hooks\").policy\n\n    @property","sourceCodeStart":1041,"sourceCodeEnd":1077,"githubUrl":"https://github.com/MemPalace/mempalace/blob/06cb6987f02610784fefbad4b2bd5d026d164ba6/mempalace/config.py#L1041-L1077","documentation":"WriteRoutingError raised during hooks-scope write-routing resolution when the config file's top-level 'hooks' key exists but is not an object. It is only checked for scope=='hooks' because that path additionally consults the legacy hooks.daemon boolean candidate. None is treated as absent; any non-dict (string, list, number) fails at mempalace/config.py:1059.","triggerScenarios":"Setting hooks: true or hooks: \"enabled\" in the config file instead of an object such as {\"daemon\": true}; a YAML list under hooks:; flattening the hooks block during manual config cleanup; scope='hooks' being the default for hook-side callers so any hook run then trips this.","commonSituations":"Migrating from an older flat config layout where hook settings were scalars; editing config while following docs for a different version; sed/regex config edits that mangle nesting.","solutions":["Change the hooks key to an object, e.g. {\"daemon\": true}, or delete the key to use defaults.","Check indentation in YAML — a nested key de-indented to column 0 turns hooks into a scalar.","If you meant the write-routing policy only, put it under write_routing.default, not under hooks."],"exampleFix":"# before (mempalace.json)\n\"hooks\": true\n\n# after\n\"hooks\": {\"daemon\": true}","handlingStrategy":"validation","validationCode":"import json\n\ncfg = json.load(open(\"mempalace.json\"))\nhooks = cfg.get(\"hooks\", {})\nif hooks is not None and not isinstance(hooks, dict):\n    raise SystemExit(\"hooks must be an object, e.g. {\\\"daemon\\\": true}\")","typeGuard":"def is_hooks_block(value: object) -> bool:\n    return value is None or isinstance(value, dict)","tryCatchPattern":"try:\n    policy = config.write_routing_policy(\"hooks\")\nexcept WriteRoutingError as exc:\n    print(f\"config error: {exc}\")  # config file issue; fix the file, don't retry","preventionTips":["Watch YAML indentation when editing the hooks block.","Run a config smoke-check (python -c 'import mempalace.config ...') after manual edits.","Keep hook flags inside the hooks object, not as top-level scalars."],"tags":["config","hooks","write-routing","file-format"],"backgroundTag":null,"analyzedSha":"06cb6987f02610784fefbad4b2bd5d026d164ba6","analyzedAt":"2026-08-15T03:03:36.213Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}