{"record":{"id":"d8f9cb47b613ad25","repo":"NousResearch/hermes-agent","slug":"metadata-must-be-json-serializable","errorCode":null,"errorMessage":"metadata must be JSON-serializable.","messagePattern":"metadata must be JSON-serializable\\.","errorType":"exception","errorClass":"SubagentLifecycleError","httpStatus":null,"severity":"error","filePath":"agent/subagent_lifecycle.py","lineNumber":523,"sourceCode":"            raise SubagentLifecycleError(\"role must be 'leaf' or 'orchestrator'.\")\n        if request.timeout_seconds is not None:\n            raise SubagentLifecycleError(\n                \"Per-launch timeout is not supported; configure delegation timeout explicitly.\"\n            )\n        if request.working_directory is not None:\n            raise SubagentLifecycleError(\n                \"working_directory is not supported because Hermes delegates use isolated task environments.\"\n            )\n        if request.blocked_tools:\n            raise SubagentLifecycleError(\n                \"Per-tool blocking is not supported; use allowed_toolsets. Hermes always blocks unsafe child tools.\"\n            )\n        try:\n            metadata_bytes = len(\n                json.dumps(dict(request.metadata), sort_keys=True).encode()\n            )\n        except (TypeError, ValueError) as exc:\n            raise SubagentLifecycleError(\"metadata must be JSON-serializable.\") from exc\n        if metadata_bytes > _MAX_METADATA_BYTES:\n            raise SubagentLifecycleError(\"metadata exceeds 8192 bytes.\")\n        if request.allowed_toolsets:\n            from toolsets import TOOLSETS\n\n            unknown = set(request.allowed_toolsets) - set(TOOLSETS)\n            if unknown:\n                raise SubagentLifecycleError(\n                    f\"Unknown toolsets: {', '.join(sorted(unknown))}.\"\n                )\n            enabled = getattr(parent, \"enabled_toolsets\", None)\n            if enabled is not None and not set(request.allowed_toolsets).issubset(\n                set(enabled)\n            ):\n                raise SubagentLifecycleError(\n                    \"Requested toolsets would broaden parent permissions.\"\n                )\n","sourceCodeStart":505,"sourceCodeEnd":541,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/agent/subagent_lifecycle.py#L505-L541","documentation":"Validation error from SubagentLifecycleManager._validate_request(): request.metadata must be JSON-serializable because the manager json.dumps()s it (sorted keys) both to measure size and to store it. If serialization raises TypeError/ValueError (sets, custom objects, datetime, recursive refs), the launch is rejected.","triggerScenarios":"Passing metadata containing set(), datetime objects, dataclass instances, numpy types, or objects without a JSON representation; metadata values coming straight from ORM/model objects; circular references in nested dicts.","commonSituations":"Stuffing ORM rows or pydantic-adjacent objects into metadata; timestamp fields left as datetime instead of ISO strings; third-party types (Decimal, UUID, tuples are fine in json but sets are not) leaking in from config parsing.","solutions":["Pre-serialize metadata yourself: metadata = json.loads(json.dumps(metadata, default=str)) to coerce or fail fast with a clear error.","Convert known non-JSON types explicitly (str(uuid), isoformat() for datetimes, sorted(list(s)) for sets).","Keep metadata to plain str/int/float/bool/None/list/dict composed of the same."],"exampleFix":"# before\nrequest = SubagentLaunchRequest(goal=g, metadata={\"tags\": {\"a\", \"b\"}, \"at\": datetime.now()})\n\n# after\nrequest = SubagentLaunchRequest(\n    goal=g,\n    metadata={\"tags\": [\"a\", \"b\"], \"at\": datetime.now().isoformat()},\n)","handlingStrategy":"validation","validationCode":"import json\ntry:\n    json.dumps(metadata, sort_keys=True)\nexcept (TypeError, ValueError):\n    metadata = json.loads(json.dumps(metadata, default=str))  # or fix at source\nrequest = SubagentLaunchRequest(goal=goal, metadata=metadata)","typeGuard":"import json\n\ndef is_json_serializable(value: object) -> bool:\n    try:\n        json.dumps(value, sort_keys=True)\n    except (TypeError, ValueError):\n        return False\n    return True","tryCatchPattern":null,"preventionTips":["Keep metadata to plain JSON types (str/int/float/bool/None/list/dict).","Convert datetime->isoformat, UUID->str, set->sorted list before launch.","Round-trip metadata through json.dumps(default=str) as a final sanitizer."],"tags":["subagents","delegation","validation","json","metadata"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}