nautechsystems/nautilus_trader · error · ValueError

Chart function must be callable, was {type(f)}

Error message

Chart function must be callable, was {type(f)}

What it means

Username and password were supplied but the Betfair application key is absent. The app key is sent as the X-Application header on every API call and is mandatory alongside the session token, so BetfairCredential::resolve refuses to build a credential without it. Distinct from error 102: here login credentials exist, only the app key is missing.

Source

Thrown at python/nautilus_trader/analysis/tearsheet.py:288

    ... def create_custom_chart(returns: pd.Series, **kwargs) -> go.Figure:
    ...     fig = go.Figure()
    ...     # ... custom visualization logic
    ...     return fig
    >>>
    >>> # Or called directly
    >>> register_chart("another_chart", create_custom_chart)

    """
    _require_not_none(name, "name")

    if not name.strip():
        raise ValueError("Chart name cannot be empty")

    if func is None:

        def decorator(f: Callable) -> Callable:
            if not callable(f):
                raise ValueError(f"Chart function must be callable, was {type(f)}")
            _CHART_REGISTRY[name] = f
            return f

        return decorator

    if not callable(func):
        raise ValueError(f"Chart function must be callable, was {type(func)}")

    _CHART_REGISTRY[name] = func
    return None


def get_chart(name: str) -> Callable:
    """
    Get registered chart function by name.

    Parameters
    ----------

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Generate an app key in the Betfair developer portal and set BETFAIR_APP_KEY or the app_key config field
  2. Confirm the app key is activated and matches the account (live vs delayed)
  3. Include the app key in the startup preflight alongside username and password

Example fix

// before
.username("better".into())
.password("secret".into()) // no app_key anywhere

// after
.username("better".into())
.password("secret".into())
.app_key("xxxxxxx".into())
Defensive patterns

Strategy: validation

Validate before calling

anyhow::ensure!(
    app_key.is_some() || std::env::var("BETFAIR_APP_KEY").is_ok(),
    "app key missing: set BETFAIR_APP_KEY or the app_key config field"
);

Type guard

fn app_key_available(app_key: Option<&str>) -> bool {
    app_key.is_some() || std::env::var("BETFAIR_APP_KEY").is_ok()
}

Prevention

When it happens

Trigger: Config provides username and password but no app_key while BETFAIR_APP_KEY is unset; using a differently named variable (e.g. BETFAIR_API_KEY) that does not match; new developer account where no app key has been generated yet.

Common situations: App key generated in the Betfair developer portal but never exported; delayed vs live key confusion with different variable names; secrets rotated and the app key entry dropped.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16). Data as JSON: /api/errors/5fbb5030192628c6. Report an issue: GitHub.