langchain-ai/deepagents · error · TypeError

`{field_name}` must be a mapping, got {type(value).__name__}

Error message

`{field_name}` must be a mapping, got {type(value).__name__}

What it means

_coerce_str_mapping validates tool_description_overrides-style fields. If the value is not a Mapping (and not None), a TypeError is raised naming the field and the actual type. This ensures override tables are dict-shaped before per-key string validation.

Source

Thrown at libs/deepagents/deepagents/profiles/harness/harness_profiles.py:833

Derived from the dataclass fields for drift-free parity with the class.
"""


def _coerce_str_or_none(value: object, field_name: str) -> str | None:
    """Validate that `value` is a string or `None` for dict-loaded string fields."""
    if value is None or isinstance(value, str):
        return value
    msg = f"`{field_name}` must be str or None, got {type(value).__name__}"
    raise TypeError(msg)


def _coerce_str_mapping(value: object, field_name: str) -> dict[str, str]:
    """Validate that `value` is a `str -> str` mapping (or `None`) and return a plain dict."""
    if value is None:
        return {}
    if not isinstance(value, Mapping):
        msg = f"`{field_name}` must be a mapping, got {type(value).__name__}"
        raise TypeError(msg)
    out: dict[str, str] = {}
    for key, val in value.items():
        if not isinstance(key, str) or not isinstance(val, str):
            msg = f"`{field_name}` keys and values must be strings"
            raise TypeError(msg)
        out[key] = val
    return out


def _coerce_frozen_strset(value: object, field_name: str) -> frozenset[str]:
    """Validate that `value` is an iterable of strings (or `None`)."""
    if value is None:
        return frozenset()
    if not isinstance(value, (list, tuple, set, frozenset)):
        msg = f"`{field_name}` must be a list/set of strings, got {type(value).__name__}"
        raise TypeError(msg)
    entries: list[str] = []
    for entry in value:

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Pass a plain dict {str: str} for the field.
  2. Convert list-of-pairs input with dict(pairs) before the call.
  3. Fix the config file so the field is a mapping (key: value), not a string or array.

Example fix

// before
config = HarnessProfileConfig.from_dict({"tool_description_overrides": [("bash", "Run shell")]} )
// after
config = HarnessProfileConfig.from_dict({"tool_description_overrides": {"bash": "Run shell"}})
Defensive patterns

Strategy: type-guard

Validate before calling

overrides = data.get("tool_description_overrides")
if overrides is not None and not isinstance(overrides, Mapping):
    data["tool_description_overrides"] = dict(overrides) if isinstance(overrides, list) else {}

Type guard

def is_str_mapping(value: object) -> TypeGuard[dict[str, str]]:
    return isinstance(value, Mapping) and all(
        isinstance(k, str) and isinstance(v, str) for k, v in value.items()
    )

Try / catch

try:
    config = HarnessProfileConfig.from_dict(data)
except TypeError as e:
    if "must be a mapping" in str(e):
        field = str(e).split('`')[1]
        data[field] = dict(data[field])
        config = HarnessProfileConfig.from_dict(data)
    else:
        raise

Prevention

When it happens

Trigger: Passing a list of pairs, a string, or a scalar where a str->str mapping is expected — e.g. from_dict({"tool_description_overrides": [("bash", "Run bash")]}), to_dict on a profile with a corrupted field, or from_harness_profile with a non-mapping override.

Common situations: Building overrides as a list of tuples in Python instead of a dict; YAML/JSON configs where the mapping was accidentally flattened to a string; merging override sources that produced a non-dict value.

Related errors


AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29). Data as JSON: /api/errors/980e325998adf068. Report an issue: GitHub.