langchain-ai/deepagents · error · TypeError

`general_purpose_subagent` must be a mapping, got {type(valu

Error message

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

What it means

_coerce_general_purpose_subagent converts the general_purpose_subagent value during from_dict. None maps to no subagent, a Mapping is delegated to GeneralPurposeSubagentProfile.from_dict, and anything else raises a TypeError naming the actual type. This enforces the nested-profile schema.

Source

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

        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:
        if not isinstance(entry, str):
            msg = f"`{field_name}` entries must be strings, got {type(entry).__name__} ({entry!r})"
            raise TypeError(msg)
        entries.append(entry)
    return frozenset(entries)


def _coerce_general_purpose_subagent(value: object) -> GeneralPurposeSubagentProfile | None:
    """Validate and construct a `GeneralPurposeSubagentProfile` from a dict value."""
    if value is None:
        return None
    if isinstance(value, Mapping):
        return GeneralPurposeSubagentProfile.from_dict(cast("Mapping[str, Any]", value))
    msg = f"`general_purpose_subagent` must be a mapping, got {type(value).__name__}"
    raise TypeError(msg)


def _validate_config_middleware_string(entry: object, field_name: str) -> None:
    """Validate grammar of a string `excluded_middleware` entry.

    Runs at `HarnessProfile` / `HarnessProfileConfig` construction so malformed
    entries fail immediately rather than at assembly time. Checks:

    - Entry is a non-empty string.
    - Entry does not contain `:`. Class-path (`module:Class`) entries are
        reserved for a future revision and rejected upfront so config files
        don't accumulate ambiguous shapes.
    - Entry does not start with `_`. Private middleware classes live outside
        the public exclusion surface.

    Scaffolding-class/name rejection and matched-something coverage are
    deliberately NOT checked here — those need the fully assembled middleware
    stack.

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Provide a mapping of GeneralPurposeSubagentProfile fields, e.g. {"model": ..., "prompt": ...}.
  2. Pass None to omit the general-purpose subagent entirely.
  3. If you already have a GeneralPurposeSubagentProfile, serialize it with its to_dict() first, then pass the dict.

Example fix

// before
config = HarnessProfileConfig.from_dict({"general_purpose_subagent": "default"})
// after
config = HarnessProfileConfig.from_dict({"general_purpose_subagent": {"model": "claude-sonnet-4-5", "prompt": "Research task"}})
Defensive patterns

Strategy: type-guard

Validate before calling

gp = data.get("general_purpose_subagent")
if gp is not None and not isinstance(gp, Mapping):
    data["general_purpose_subagent"] = gp.to_dict() if hasattr(gp, "to_dict") else None

Type guard

def is_gp_subagent_dict(value: object) -> TypeGuard[dict[str, Any] | None]:
    return value is None or isinstance(value, Mapping)

Try / catch

try:
    config = HarnessProfileConfig.from_dict(data)
except TypeError as e:
    if "general_purpose_subagent must be a mapping" in str(e):
        data["general_purpose_subagent"] = data["general_purpose_subagent"].to_dict()
        config = HarnessProfileConfig.from_dict(data)
    else:
        raise

Prevention

When it happens

Trigger: Calling HarnessProfileConfig.from_dict with general_purpose_subagent set to a string, list, or object, e.g. {"general_purpose_subagent": "default"} instead of a mapping of subagent profile fields.

Common situations: Configs written as a named string shorthand for a built-in subagent; passing an already-constructed GeneralPurposeSubagentProfile object into from_dict; YAML config where the nested block was collapsed to a scalar.

Related errors


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