Skip to content

Bug: _get_provider_base_url() crashes when providers is defined as a list in config.yaml #3979

@Fillaus

Description

@Fillaus

Bug: _get_provider_base_url() crashes when providers is defined as a list in config.yaml

Description

The function _get_provider_base_url(provider_id) in api/config.py (line ~1921) assumes that cfg["providers"] is always a dict. When the user defines it as a list instead (providers: []), the function crashes with:

AttributeError: 'list' object has no attribute 'get'

Reproduction

  1. In config.yaml, set: providers: [] (empty list)
  2. Start a chat that triggers _get_provider_base_url()
  3. Result: AttributeError: 'list' object has no attribute 'get'

Root Cause

The line:

prov_cfg = cfg.get("providers", {}).get(provider_id, {}) or {}

only works with a dict. An empty list [] is semantically identical to {} ("no providers configured"), but the code doesn't handle both forms.

Fix

def _get_provider_base_url(provider_id):
    """Look up the configured base_url for a provider (e.g. lmstudio).

    Checks two locations, in order:
      1. ``cfg["providers"][<provider_id>]["base_url"]`` — the explicit
       per-provider override.
      2. ``cfg["model"]["base_url"]`` — falls back here when
       ``cfg["model"]["provider"] == provider_id``. This is the historical
       shape (the model block carries both the active provider AND the
       base URL for that provider in a single record).

    Returns the URL stripped of trailing ``/`` if configured, otherwise None.
    """
    prov_raw = cfg.get("providers", {})
    # providers can be a dict (provider_id -> config) or a list of provider entries
    if isinstance(prov_raw, dict):
        prov_cfg = prov_raw.get(provider_id, {}) or {}
    elif isinstance(prov_raw, list):
        # Search for matching provider in list format
        prov_cfg = None
        for entry in prov_raw:
            if isinstance(entry, dict):
                entry_id = str(entry.get("id", "") or entry.get("name", "")).strip().lower()
                if entry_id == str(provider_id).strip().lower():
                    prov_cfg = entry
                    break
        prov_cfg = prov_cfg or {}
    else:
        prov_cfg = {}
    explicit = (prov_cfg.get("base_url") or "").strip().rstrip("/")
    if explicit:
        return explicit
    model_cfg = cfg.get("model", {}) or {}
    if isinstance(model_cfg, dict):
        model_provider = str(model_cfg.get("provider") or "").strip().lower()
        if model_provider == str(provider_id).strip().lower():
            model_base = (model_cfg.get("base_url") or "").strip().rstrip("/")
            if model_base:
                return model_base
    return None

Background

The bug has existed since commit 35cf332c ("feat: add LM Studio provider support with live model discovery"). Since then, 125+ commits touched config.py, but no one caught the list-form edge case.

Most users either have {} or don't set providers explicitly at all. Only in rare cases is a list used — and then the chat crashes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingsprint-candidateStrong candidate for next sprint

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions