{"record":{"id":"d80ca27c5271f06c","repo":"nautechsystems/nautilus_trader","slug":"name-must-not-be-none","errorCode":null,"errorMessage":"{name} must not be None","messagePattern":"(.+?) must not be None","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/nautilus_trader/analysis/tearsheet.py","lineNumber":92,"sourceCode":"\n    PLOTLY_AVAILABLE = True\nexcept ImportError:\n    PLOTLY_AVAILABLE = False\n\n    if not TYPE_CHECKING:\n        go = None  # type: ignore[assignment]\n\n\nTRADING_DAYS_PER_YEAR = 252\n\n_STATIC_IMAGE_SUFFIXES = frozenset({\".png\", \".jpg\", \".jpeg\", \".webp\", \".svg\", \".pdf\"})\n\n_CHART_REGISTRY: dict[str, Callable] = {}\n\n\ndef _require_not_none(value: Any, name: str) -> None:\n    if value is None:\n        raise ValueError(f\"{name} must not be None\")\n\n\ndef _format_optional_iso8601(timestamp_ns: int | None) -> str:\n    if timestamp_ns is None:\n        return \"N/A\"\n    return unix_nanos_to_iso8601(timestamp_ns, nanos_precision=False)\n\n\ndef _format_optional_duration(start_ns: int | None, end_ns: int | None) -> str:\n    if start_ns is None or end_ns is None:\n        return \"N/A\"\n    return str(pd.Timedelta(end_ns - start_ns, unit=\"ns\"))\n\n\ndef _to_returns_series(returns) -> pd.Series:\n    pandas = _require_pandas()\n    if returns is None:\n        return pandas.Series(dtype=float)","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/a4b06ed870971b5671d12754ea138a3ab99b1dec/python/nautilus_trader/analysis/tearsheet.py#L74-L110","documentation":"A username was supplied but no password, so the credential triple is incomplete and BetfairCredential::resolve rejects it instead of attempting a doomed login. Note the resolution order: when any value is provided in config, the environment fallback is skipped entirely for the triple, so setting only username in config disables BETFAIR_PASSWORD from the environment.","triggerScenarios":"Config sets username while password is left unset (expecting env fallback, which does not happen for partial config); or the environment has only BETFAIR_USERNAME set; password key misspelled in a YAML config.","commonSituations":"Splitting credentials between config and environment; renaming credential keys during refactors; a secret manager injecting only some variables; partially migrated config files.","solutions":["Provide the password alongside the username (config field or BETFAIR_PASSWORD)","Supply all three credentials from one source - all in config or all via environment - to avoid partial resolution","Assert credential completeness at startup before building the client"],"exampleFix":"// before\n.username(\"better\".into()) // password assumed from env (never read)\n\n// after\n.username(\"better\".into())\n.password(\"secret\".into())\n.app_key(\"xxxxxxx\".into())","handlingStrategy":"validation","validationCode":"anyhow::ensure!(\n    username.is_some() == password.is_some(),\n    \"username and password must be provided together (got username={:?})\",\n    username\n);","typeGuard":"fn credential_pair_complete(username: Option<&str>, password: Option<&str>) -> bool {\n    username.is_some() == password.is_some()\n}","tryCatchPattern":null,"preventionTips":["Never split the credential triple between config and environment","Treat partial credentials as a config bug caught in review","Log which credential source (config vs env) was used at startup"],"tags":["betfair","credentials","config","validation","rust"],"backgroundTag":"incomplete-credentials","analyzedSha":"a4b06ed870971b5671d12754ea138a3ab99b1dec","analyzedAt":"2026-08-16T22:54:50.089Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}