MemPalace/mempalace · error · WriteRoutingError

config hooks must be an object

Error message

config hooks must be an object

What it means

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.

Source

Thrown at mempalace/config.py:1059

            [
                RoutingPolicyCandidate(
                    f"config write_routing.{normalized_scope}",
                    routing_config.get(normalized_scope),
                ),
                RoutingPolicyCandidate(
                    "config write_routing.default",
                    routing_config.get("default"),
                ),
            ]
        )

        if normalized_scope == "hooks":
            hooks_config = self._file_config.get("hooks", {})
            if hooks_config is None:
                hooks_config = {}

            if not isinstance(hooks_config, dict):
                raise WriteRoutingError("config hooks must be an object")

            candidates.append(
                RoutingPolicyCandidate(
                    "config hooks.daemon (legacy)",
                    hooks_config.get("daemon"),
                    legacy_boolean=True,
                )
            )

        return resolve_write_routing_policy(candidates)

    @property
    def hook_write_routing(self) -> WriteRoutingPolicy:
        """Resolved future routing policy for hook-triggered writes."""

        return self.resolve_write_routing("hooks").policy

    @property

View on GitHub (pinned to 06cb6987f0)

Solutions

  1. Change the hooks key to an object, e.g. {"daemon": true}, or delete the key to use defaults.
  2. Check indentation in YAML — a nested key de-indented to column 0 turns hooks into a scalar.
  3. If you meant the write-routing policy only, put it under write_routing.default, not under hooks.

Example fix

# before (mempalace.json)
"hooks": true

# after
"hooks": {"daemon": true}
Defensive patterns

Strategy: validation

Validate before calling

import json

cfg = json.load(open("mempalace.json"))
hooks = cfg.get("hooks", {})
if hooks is not None and not isinstance(hooks, dict):
    raise SystemExit("hooks must be an object, e.g. {\"daemon\": true}")

Type guard

def is_hooks_block(value: object) -> bool:
    return value is None or isinstance(value, dict)

Try / catch

try:
    policy = config.write_routing_policy("hooks")
except WriteRoutingError as exc:
    print(f"config error: {exc}")  # config file issue; fix the file, don't retry

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of MemPalace/mempalace@06cb6987f0 (2026-08-15). Data as JSON: /api/errors/d953887af10242e0. Report an issue: GitHub.