{"record":{"id":"3f48b85c55dc6d84","repo":"HKUDS/Vibe-Trading","slug":"metadata-must-be-a-mapping-got-type-self-metadat","errorCode":null,"errorMessage":"metadata must be a mapping, got {type(self.metadata).__name__}","messagePattern":"metadata must be a mapping, got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/entities/cashflow.py","lineNumber":181,"sourceCode":"            raise ValueError(\n                f\"amount must be a finite number, got {self.amount!r}; a missing \"\n                \"value must be fixed at the source, not carried as NaN\"\n            )\n        object.__setattr__(self, \"amount\", amount)\n\n        required_sign = KIND_DIRECTION.get(self.kind)\n        if required_sign is not None and amount != 0.0:\n            if (amount > 0) != (required_sign > 0):\n                direction = \"positive (cash in)\" if required_sign > 0 else \"negative (cash out)\"\n                raise ValueError(\n                    f\"kind={self.kind!r} must have a {direction} amount under the \"\n                    f\"holder-perspective sign convention, got {amount!r}. Flip the \"\n                    \"sign, or use a distinct kind if this flow is genuinely \"\n                    \"two-directional (e.g. 'recallable_distribution').\"\n                )\n\n        if not isinstance(self.metadata, Mapping):\n            raise ValueError(\n                f\"metadata must be a mapping, got {type(self.metadata).__name__}\"\n            )\n        object.__setattr__(self, \"metadata\", MappingProxyType(dict(self.metadata)))\n\n    @property\n    def is_valuation(self) -> bool:\n        \"\"\"Whether this record is a mark rather than a settled cash movement.\n\n        Returns:\n            True when ``kind`` is one of ``VALUATION_KINDS``.\n        \"\"\"\n        return self.kind in VALUATION_KINDS\n\n\n@dataclass(frozen=True)\nclass CashFlowSeries:\n    \"\"\"An ordered, immutable collection of ``CashFlow`` in one currency.\n","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/entities/cashflow.py#L163-L199","documentation":"CashFlow.metadata must be any Mapping (dict, MappingProxy, etc.); anything else (list, string, None) is rejected. The entity then freezes it into an immutable MappingProxyType to guarantee flows are hashable-ish and tamper-proof.","triggerScenarios":"CashFlow(..., metadata=['tag1','tag2']) or metadata=None or metadata='note' — any non-Mapping value.","commonSituations":"Passing JSON-loaded values where metadata arrived as a list; a default of None from an optional parser branch; forgetting the field expects key-value pairs.","solutions":["Pass a dict (or omit metadata entirely, which uses the default empty mapping)","If metadata comes from external data, coerce non-mappings or reject them at ingest"],"exampleFix":"# before\nCashFlow(..., metadata='imported')\n# after\nCashFlow(..., metadata={'source': 'imported'})","handlingStrategy":"type-guard","validationCode":"metadata = row.get('metadata') if isinstance(row.get('metadata'), Mapping) else {}","typeGuard":"from collections.abc import Mapping\ndef is_valid_metadata(v) -> bool:\n    return isinstance(v, Mapping)","tryCatchPattern":"try:\n    CashFlow(..., metadata=md)\nexcept ValueError as e:\n    if 'metadata must be a mapping' in str(e):\n        md = {'value': md}  # coerce or drop","preventionTips":["Default metadata to {} at call sites","Validate external JSON shapes before entity construction"],"tags":["python","cashflow","metadata","type-validation"],"backgroundTag":"wrong-type-for-field","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}