HKUDS/Vibe-Trading · error · Trading212ConfigError

profile must be 'paper', 'live-readonly' or 'live'

Error message

profile must be 'paper', 'live-readonly' or 'live'

What it means

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.

Source

Thrown at agent/src/trading/connectors/trading212/sdk.py:78

        timeout: Network timeout in seconds.
        readonly: Always true for built-in profiles; order methods refuse all
            requests regardless of this flag.
    """

    api_key: str = ""
    api_secret: str = ""
    profile: str = "live-readonly"
    base_url: str = DEFAULT_BASE_URL
    timeout: float = 15.0
    readonly: bool = True

    @classmethod
    def from_mapping(cls, data: Mapping[str, Any] | None = None) -> "Trading212Config":
        """Build a config from a JSON-like mapping, normalizing profile/URL."""
        payload = dict(data or {})
        profile = str(payload.get("profile") or "live-readonly").strip().lower()
        if profile not in PROFILE_ENVIRONMENTS:
            raise Trading212ConfigError("profile must be 'paper', 'live-readonly' or 'live'")
        base_url = str(payload.get("base_url") or DEFAULT_BASE_URL).strip().rstrip("/")
        if not base_url.startswith(("http://", "https://")):
            raise Trading212ConfigError("base_url must start with http:// or https://")
        return cls(
            api_key=str(payload.get("api_key") or "").strip(),
            api_secret=str(payload.get("api_secret") or "").strip(),
            profile=profile,
            base_url=base_url,
            timeout=float(payload.get("timeout") or 15.0),
            readonly=bool(payload.get("readonly", True)),
        )

    def with_overrides(
        self,
        *,
        api_key: str | None = None,
        api_secret: str | None = None,
        profile: str | None = None,

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Use 'paper' for Trading212 demo accounts (not 'demo'), or 'live-readonly'/'live' for real accounts
  2. Remove the profile key to accept the safe default 'live-readonly'
  3. Fix separator variants: 'live readonly' or 'live_readonly' -> 'live-readonly'

Example fix

// before
{"profile": "demo", "api_key": "..."}

// after
{"profile": "paper", "api_key": "..."}
Defensive patterns

Strategy: validation

Validate before calling

ALLOWED = {'paper', 'live-readonly', 'live'}

profile = str(data.get('profile') or 'live-readonly').strip().lower()
assert profile in ALLOWED, f"bad profile: {profile!r}"
cfg = Trading212Config.from_mapping(data)

Type guard

def is_valid_t212_profile(value: object) -> bool:
    return isinstance(value, str) and value.strip().lower() in {'paper', 'live-readonly', 'live'}

Try / catch

try:
    cfg = Trading212Config.from_mapping(raw)
except Trading212ConfigError as exc:
    if 'profile' in str(exc):
        raw['profile'] = 'live-readonly'  # safe default
        cfg = Trading212Config.from_mapping(raw)
    else:
        raise

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Understand the failure class

Background: Config validation failed: what "invalid value for {key}" and settings-rejection errors mean across 19 open-source libraries — this error's family across 19 libraries.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/b9cf6689cc3a50fd. Report an issue: GitHub.