{"record":{"id":"5dc0c7be5c79124e","repo":"HKUDS/Vibe-Trading","slug":"cash-flow-kind-flow-kind-r-on-flow-date-is-cla","errorCode":null,"errorMessage":"cash-flow kind {flow.kind!r} on {flow.date} is classified neither external nor internal, so it cannot be treated as either. Pass external_kinds=[...] or internal_kinds=[...] to say which side of the portfolio boundary it crosses. Known external: {', '.join(sorted(external))}. Known internal: {', '.join(sorted(internal))}.","messagePattern":"cash-flow kind (.+?) on (.+?) is classified neither external nor internal, so it cannot be treated as either\\. Pass external_kinds=\\[\\.\\.\\.\\] or internal_kinds=\\[\\.\\.\\.\\] to say which side of the portfolio boundary it crosses\\. Known external: (.+?)\\. Known internal: (.+?)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/quantlib/performance.py","lineNumber":413,"sourceCode":"        DEFAULT_INTERNAL_KINDS\n        if internal_kinds is None\n        else frozenset(normalize_kind(kind) for kind in internal_kinds)\n    )\n    overlap = external & internal\n    if overlap:\n        raise ValueError(\n            \"a kind cannot be both external and internal; overlapping: \"\n            f\"{', '.join(sorted(overlap))}\"\n        )\n\n    selected: list[tuple[date, float]] = []\n    for flow in flows:\n        if flow.is_valuation:\n            continue\n        if flow.kind in external:\n            selected.append((flow.date, -flow.amount))\n        elif flow.kind not in internal:\n            raise ValueError(\n                f\"cash-flow kind {flow.kind!r} on {flow.date} is classified \"\n                \"neither external nor internal, so it cannot be treated as \"\n                \"either. Pass external_kinds=[...] or internal_kinds=[...] to \"\n                \"say which side of the portfolio boundary it crosses. Known \"\n                f\"external: {', '.join(sorted(external))}. Known internal: \"\n                f\"{', '.join(sorted(internal))}.\"\n            )\n    selected.sort(key=lambda item: item[0])\n    return tuple(selected)\n\n\ndef _annualize(total_return: float, days: int) -> float | None:\n    \"\"\"Annualise a holding-period return, or decline to when the window is short.\n\n    Args:\n        total_return: Return over the whole window, as a decimal fraction.\n        days: Calendar days spanned by the window.\n","sourceCodeStart":395,"sourceCodeEnd":431,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/quantlib/performance.py#L395-L431","documentation":"Every non-valuation cash flow must be classified as external or internal before return math can treat it. This error means a flow's kind is in neither the external nor the internal set, and the library refuses to silently guess which side of the portfolio boundary it crosses, because guessing wrong would corrupt the return.","triggerScenarios":"Calling time_weighted_return / modified_dietz_return / money_weighted_return / external_flows with a flow whose kind string (after normalization, e.g. lowercase) is not in DEFAULT_EXTERNAL_KINDS, DEFAULT_INTERNAL_KINDS, or the sets you passed explicitly — e.g. kind='crypto_transfer' or a typo like 'contribution '.","commonSituations":"A new cash-flow kind introduced by an upstream system; typos or casing/whitespace differences in kind strings; a data migration that renamed kinds ('deposit' -> 'client_deposit'); version upgrades that changed the default kind sets.","solutions":["Pass external_kinds=[...] or internal_kinds=[...] including the unknown kind shown in the message, choosing the side of the boundary it actually crosses.","Fix the kind string at the source (typo, casing, whitespace) so it matches a known kind.","Extend your ingestion layer with a whitelist validation so unclassified kinds are caught at import time, not during analytics."],"exampleFix":"# before\ntime_weighted_return(valuations=marks, flows=flows)  # flows contain kind='wire_in' -> ValueError\n\n# after\ntime_weighted_return(\n    valuations=marks,\n    flows=flows,\n    external_kinds=['contribution', 'withdrawal', 'wire_in'],\n)","handlingStrategy":"validation","validationCode":"KNOWN = DEFAULT_EXTERNAL_KINDS | DEFAULT_INTERNAL_KINDS | set(external_kinds or ()) | set(internal_kinds or ())\nunknown = [f for f in flows if not f.is_valuation and normalize_kind(f.kind) not in KNOWN]\nassert not unknown, f'unclassified kinds: {[(f.kind, f.date) for f in unknown]}'","typeGuard":"def flows_fully_classified(flows, external, internal) -> bool:\n    return all(f.is_valuation or normalize_kind(f.kind) in (external | internal) for f in flows)","tryCatchPattern":"try:\n    r = time_weighted_return(valuations=marks, flows=flows)\nexcept ValueError as e:\n    if 'neither external nor internal' in str(e):\n        r = time_weighted_return(valuations=marks, flows=flows,\n                                 external_kinds=[*default_ext, 'wire_in'])\n    else:\n        raise","preventionTips":["Validate kind strings at the ingestion boundary against a whitelist.","Fail CI tests when a new kind appears unclassified.","Normalize casing/whitespace of kinds before analytics."],"tags":["quantlib","cash-flows","unclassified-kind","input-validation"],"backgroundTag":"unknown-enum-value","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}