{"record":{"id":"0e7746b1ef60a3a8","repo":"HKUDS/Vibe-Trading","slug":"at-most-50-portfolio-sources-are-allowed","errorCode":null,"errorMessage":"at most 50 portfolio sources are allowed","messagePattern":"at most 50 portfolio sources are allowed","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"agent/src/portfolio/config.py","lineNumber":154,"sourceCode":"            when omitted.\n\n    Returns:\n        Validated, order-normalized settings.\n\n    Raises:\n        ValueError: If the display currency, the source list, a connection id,\n            a label, or a referenced profile's eligibility is invalid.\n    \"\"\"\n    store = connection_store or ConnectionStore()\n    currency = str(payload.get(\"display_currency\") or \"USD\").strip().upper()\n    if currency not in {\"USD\", \"CNY\"}:\n        raise ValueError(\"display_currency must be USD or CNY\")\n\n    raw_sources = payload.get(\"sources\")\n    if not isinstance(raw_sources, list):\n        raise ValueError(\"sources must be a list\")\n    if len(raw_sources) > 50:\n        raise ValueError(\"at most 50 portfolio sources are allowed\")\n\n    seen_ids: set[str] = set()\n    sources: list[PortfolioSource] = []\n    for index, raw in enumerate(raw_sources):\n        if not isinstance(raw, dict):\n            raise ValueError(\"each portfolio source must be an object\")\n        connection_id = (\n            str(raw.get(\"connection_id\") or raw.get(\"id\") or \"\").strip().lower()\n        )\n        if not _SOURCE_ID_RE.fullmatch(connection_id):\n            raise ValueError(f\"invalid portfolio connection id: {connection_id or '?'}\")\n        legacy_profile_id = str(raw.get(\"profile_id\") or \"\").strip().lower()\n        if legacy_profile_id:\n            legacy_profile = profile_by_id(legacy_profile_id)\n            connection = store.ensure(\n                connection_id,\n                legacy_profile.id,\n                str(raw.get(\"label\") or legacy_profile.label),","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/portfolio/config.py#L136-L172","documentation":"Portfolio settings enforce a hard cap of 50 sources per configuration; len(raw_sources) > 50 raises immediately. The limit bounds fan-out of read-only portfolio aggregation per settings file.","triggerScenarios":"A settings file with 51+ entries in sources, typically produced by merging multiple configs or programmatically enumerating every exchange connection.","commonSituations":"Concatenating settings files, scripts that add a source per account/subaccount, or copying a bulk test fixture into production config.","solutions":["Prune sources to the 50 you actually need (deduplicate by connection_id first)","Split aggregation across multiple settings files/profiles if genuinely required","Audit for stale entries from previous connections before adding new ones"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"seen = set()\ndeduped = [s for s in payload['sources']\n           if (id_ := (s.get('connection_id') or s.get('id'))) not in seen and not seen.add(id_)]\npayload['sources'] = deduped[:50]\nif len(deduped) > 50:\n    log.warning('truncated sources to 50 (dropped %d)', len(deduped) - 50)","typeGuard":"def within_source_limit(payload) -> bool:\n    return isinstance(payload.get('sources'), list) and len(payload['sources']) <= 50","tryCatchPattern":"try:\n    settings = parse_settings(payload, store)\nexcept ValueError as exc:\n    if 'at most 50' in str(exc):\n        payload['sources'] = dedupe_and_cap(payload['sources'], 50)\n        settings = parse_settings(payload, store)","preventionTips":["Deduplicate merged configs by connection_id before saving","Split large account sets across multiple settings files","Audit for stale connections before adding new ones"],"tags":["python","config","limit","quota"],"backgroundTag":"config-limit-exceeded","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}