{"record":{"id":"a695df475556f425","repo":"HKUDS/Vibe-Trading","slug":"invalid-portfolio-connection-id-connection-id-or","errorCode":null,"errorMessage":"invalid portfolio connection id: {connection_id or '?'}","messagePattern":"invalid portfolio connection id: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/portfolio/config.py","lineNumber":165,"sourceCode":"    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),\n            )\n        else:\n            connection = store.get(connection_id)\n        profile = profile_by_id(connection.profile_id)\n        if profile not in eligible_profiles():\n            raise ValueError(\n                f\"connection is not eligible for read-only portfolios: {connection_id}\"\n            )\n        if connection_id in seen_ids:\n            raise ValueError(\"portfolio connection ids must be unique\")\n        label = str(raw.get(\"label\") or connection.label).strip()","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/portfolio/config.py#L147-L183","documentation":"A source's connection id (from 'connection_id' or legacy 'id', stripped and lowercased) must fully match _SOURCE_ID_RE; otherwise parsing aborts with the offending id (or '?' if empty). The regex enforces a safe slug charset for ids.","triggerScenarios":"Ids containing uppercase (lowered first), spaces, slashes, special characters, or an entirely missing connection_id/id key; also ids exceeding whatever length/charset _SOURCE_ID_RE mandates.","commonSituations":"Copy-pasting exchange account URIs or emails as ids, omitting connection_id and only providing profile_id, or trailing whitespace/punctuation from manual editing.","solutions":["Use a simple lowercase slug: letters, digits, hyphens/underscores, e.g. 'binance-main'","Ensure each entry has a non-empty connection_id (or id) key","Check _SOURCE_ID_RE in agent/src/portfolio/config.py for the exact charset/length rules"],"exampleFix":"# before\n{\"connection_id\": \"Binance Main!!\"}\n# after\n{\"connection_id\": \"binance-main\"}","handlingStrategy":"validation","validationCode":"import re\n_SOURCE_ID_RE = re.compile(r'^[a-z0-9][a-z0-9_-]*$')  # mirror the library's pattern\ncid = str(entry.get('connection_id') or entry.get('id') or '').strip().lower()\nif not _SOURCE_ID_RE.fullmatch(cid):\n    entry['connection_id'] = slugify(cid)","typeGuard":"def is_valid_source_id(cid) -> bool:\n    return isinstance(cid, str) and bool(re.fullmatch(r'[a-z0-9][a-z0-9_-]*', cid.strip().lower()))","tryCatchPattern":"try:\n    settings = parse_settings(payload, store)\nexcept ValueError as exc:\n    if 'invalid portfolio connection id' in str(exc):\n        for e in payload['sources']:\n            e['connection_id'] = slugify(e.get('connection_id') or e.get('id') or '')\n        settings = parse_settings(payload, store)","preventionTips":["Generate ids with a slugify function (lowercase, hyphens, no specials)","Always include connection_id (or legacy id) in every source entry","Mirror the library's regex in upstream form validation"],"tags":["python","config","regex","identifier"],"backgroundTag":"invalid-identifier-format","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}