{"record":{"id":"b9cf6689cc3a50fd","repo":"HKUDS/Vibe-Trading","slug":"profile-must-be-paper-live-readonly-or-live-b9cf66","errorCode":null,"errorMessage":"profile must be 'paper', 'live-readonly' or 'live'","messagePattern":"profile must be 'paper', 'live-readonly' or 'live'","errorType":"validation","errorClass":"Trading212ConfigError","httpStatus":null,"severity":"error","filePath":"agent/src/trading/connectors/trading212/sdk.py","lineNumber":78,"sourceCode":"        timeout: Network timeout in seconds.\n        readonly: Always true for built-in profiles; order methods refuse all\n            requests regardless of this flag.\n    \"\"\"\n\n    api_key: str = \"\"\n    api_secret: str = \"\"\n    profile: str = \"live-readonly\"\n    base_url: str = DEFAULT_BASE_URL\n    timeout: float = 15.0\n    readonly: bool = True\n\n    @classmethod\n    def from_mapping(cls, data: Mapping[str, Any] | None = None) -> \"Trading212Config\":\n        \"\"\"Build a config from a JSON-like mapping, normalizing profile/URL.\"\"\"\n        payload = dict(data or {})\n        profile = str(payload.get(\"profile\") or \"live-readonly\").strip().lower()\n        if profile not in PROFILE_ENVIRONMENTS:\n            raise Trading212ConfigError(\"profile must be 'paper', 'live-readonly' or 'live'\")\n        base_url = str(payload.get(\"base_url\") or DEFAULT_BASE_URL).strip().rstrip(\"/\")\n        if not base_url.startswith((\"http://\", \"https://\")):\n            raise Trading212ConfigError(\"base_url must start with http:// or https://\")\n        return cls(\n            api_key=str(payload.get(\"api_key\") or \"\").strip(),\n            api_secret=str(payload.get(\"api_secret\") or \"\").strip(),\n            profile=profile,\n            base_url=base_url,\n            timeout=float(payload.get(\"timeout\") or 15.0),\n            readonly=bool(payload.get(\"readonly\", True)),\n        )\n\n    def with_overrides(\n        self,\n        *,\n        api_key: str | None = None,\n        api_secret: str | None = None,\n        profile: str | None = None,","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/trading/connectors/trading212/sdk.py#L60-L96","documentation":"Trading212Config.from_mapping validates the 'profile' key after normalization (strip + lowercase) and rejects values outside PROFILE_ENVIRONMENTS. Unlike Tiger, the default when the key is absent/empty is 'live-readonly', so this only fires for explicitly provided unknown profile strings; it fails at config-parse time before any HTTP call.","triggerScenarios":"Calling Trading212Config.from_mapping (directly or via with_overrides, build_config, load_config) with 'profile' set to something like 'Demo', 'practice', 'prod', or 'read-only' (space instead of hyphen).","commonSituations":"Trading212's own UI calls it a 'Demo' account, so developers write 'demo' instead of 'paper'; hyphen/space variations like 'live readonly'; migrating configs between connectors with different profile vocabularies.","solutions":["Use 'paper' for Trading212 demo accounts (not 'demo'), or 'live-readonly'/'live' for real accounts","Remove the profile key to accept the safe default 'live-readonly'","Fix separator variants: 'live readonly' or 'live_readonly' -> 'live-readonly'"],"exampleFix":"// before\n{\"profile\": \"demo\", \"api_key\": \"...\"}\n\n// after\n{\"profile\": \"paper\", \"api_key\": \"...\"}","handlingStrategy":"validation","validationCode":"ALLOWED = {'paper', 'live-readonly', 'live'}\n\nprofile = str(data.get('profile') or 'live-readonly').strip().lower()\nassert profile in ALLOWED, f\"bad profile: {profile!r}\"\ncfg = Trading212Config.from_mapping(data)","typeGuard":"def is_valid_t212_profile(value: object) -> bool:\n    return isinstance(value, str) and value.strip().lower() in {'paper', 'live-readonly', 'live'}","tryCatchPattern":"try:\n    cfg = Trading212Config.from_mapping(raw)\nexcept Trading212ConfigError as exc:\n    if 'profile' in str(exc):\n        raw['profile'] = 'live-readonly'  # safe default\n        cfg = Trading212Config.from_mapping(raw)\n    else:\n        raise","preventionTips":["Map Trading212 'Demo' to 'paper' in any config generation code","Use a shared profile vocabulary/enum across connectors","Validate configs in CI with a JSON Schema enum"],"tags":["trading212","config","validation","profile"],"backgroundTag":"config-validation-failed","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}