BerriAI/litellm · error · ValueError

otel.attributes must be a mapping with optional 'include_lis

Error message

otel.attributes must be a mapping with optional 'include_list' / 'exclude_list', got {type(value).__name__}

What it means

LiteLLM's OpenTelemetry metric attribute filter is configured via 'otel.attributes', which must be a mapping (dict) optionally containing include_list/exclude_list. _build_metric_attribute_filter accepts an already-built OTELMetricAttributeFilter or a dict; any other type (str, list, tuple, or a bad YAML parse) raises ValueError naming the offending type.

Source

Thrown at litellm/integrations/opentelemetry.py:161

        "gen_ai.request.model",
        "gen_ai.framework",
        "hidden_params",
    )
    + tuple(f"metadata.{key}" for key in METRIC_METADATA_KEYS)
)


@dataclass(frozen=True)
class OTELMetricAttributeFilter:
    include_list: list[str] | None = None
    exclude_list: list[str] | None = None


def _build_metric_attribute_filter(value: Any) -> OTELMetricAttributeFilter:
    if isinstance(value, OTELMetricAttributeFilter):
        return value
    if not isinstance(value, dict):
        raise ValueError(
            "otel.attributes must be a mapping with optional 'include_list' / "
            f"'exclude_list', got {type(value).__name__}"
        )
    return OTELMetricAttributeFilter(
        include_list=value.get("include_list"),
        exclude_list=value.get("exclude_list"),
    )


def _resolve_metric_attribute_filter(
    attributes: OTELMetricAttributeFilter | None,
) -> tuple[frozenset[str] | None, frozenset[str] | None]:
    if attributes is None:
        return None, None
    include: Final = attributes.include_list or None
    exclude: Final = attributes.exclude_list or None
    if include and exclude:
        raise ValueError("otel.attributes: include_list and exclude_list are mutually exclusive")

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Use a mapping: otel: attributes: {include_list: [model, api_provider]} (or exclude_list)
  2. Check YAML indentation so 'attributes:' is a key under 'otel:' with list values beneath it
  3. Do not pass a string or list directly as the attributes value
  4. After fixing, restart the proxy; config is parsed at startup so a stale config file will keep failing

Example fix

# before (config.yaml)
otel:
  attributes: model,api_provider  # ValueError: ...got str

# after
otel:
  attributes:
    include_list:
      - model
      - api_provider
Defensive patterns

Strategy: type-guard

Validate before calling

def valid_otel_attributes(cfg: object) -> bool:
    return cfg is None or isinstance(cfg, dict) or hasattr(cfg, "include_list")

Type guard

from typing import Any

def is_otel_attributes_mapping(value: Any) -> bool:
    return isinstance(value, dict) and set(value) <= {"include_list", "exclude_list"}

Prevention

When it happens

Trigger: Writing 'otel.attributes: include_list' (a bare string) in proxy YAML; passing a list of names like 'otel.attributes: [model, endpoint]'; YAML indentation that parses attributes into a scalar instead of a mapping.

Common situations: Configuring Prometheus/OpenTelemetry metric label filtering in litellm proxy config.yaml; authors assuming a comma-separated string or flat list is accepted; YAML indentation mistakes nesting the keys under the wrong parent.

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/65d2910fb4703e7e. Report an issue: GitHub.