FoundationAgents/MetaGPT · error · ValueError

Missing fields

Error message

Missing fields: {missing_fields}

What it means

MetaGPT's count_message_tokens() estimates Chat Completion prompt size as encoded text plus model-specific per-message/per-name overhead. It implements that overhead only for an explicit set of OpenAI model IDs, the exact aliases gpt-3.5-turbo and gpt-4, the special open-llm-model value, and models whose name contains claude; every other model reaches the else branch and raises NotImplementedError. The preceding tiktoken fallback only chooses an encoding and does not make an unknown model supported.

Solutions

  1. If you use a custom or deployment model name, set llm.pricing_plan in config2.yaml to an exact supported published model with compatible tokenization (for example gpt-4o-mini); OpenAIGPT uses pricing_plan for usage calculation.
  2. If you intended to use a supported model, correct llm.model to an exact ID from the table, such as gpt-4o-mini-2024-07-18, gpt-4-turbo, gpt-4-0613, or gpt-3.5-turbo-0125; only the exact aliases gpt-3.5-turbo and gpt-4 are normalized.
  3. Upgrade MetaGPT/token_counter.py to a release whose model list includes your model, or patch the local set if you maintain a fork.
  4. For direct calls, catch NotImplementedError and count with a supported model with compatible encoding, or encode the concatenated message text with tiktoken yourself as an explicit approximation.
  5. If accurate usage accounting is not needed, set llm.calc_usage: false; this avoids the _calc_usage warning but does not make count_message_tokens support the model.

Example fix

# before (config2.yaml)
llm:
  api_type: openai
  base_url: https://your-openai-compatible-endpoint/v1
  model: my-qwen-deployment

# after: keep the served model, but count usage with a supported pricing plan
llm:
  api_type: openai
  base_url: https://your-openai-compatible-endpoint/v1
  model: my-qwen-deployment
  pricing_plan: gpt-4o-mini
Defensive patterns

Strategy: validation

Validate before calling

from metagpt.utils.token_counter import count_message_tokens

_MESSAGE_TOKEN_MODELS = {
    "gpt-3.5-turbo-0613", "gpt-3.5-turbo-16k-0613", "gpt-35-turbo",
    "gpt-35-turbo-16k", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-1106",
    "gpt-3.5-turbo-0125", "gpt-3.5-turbo-0301", "gpt-3.5-turbo",
    "gpt-4-0314", "gpt-4-32k-0314", "gpt-4-0613", "gpt-4-32k-0613",
    "gpt-4-turbo", "gpt-4-turbo-preview", "gpt-4-0125-preview",
    "gpt-4-1106-preview", "gpt-4-vision-preview", "gpt-4-1106-vision-preview",
    "gpt-4o", "gpt-4o-2024-05-13", "gpt-4o-2024-08-06",
    "gpt-4o-mini", "gpt-4o-mini-2024-07-18", "o1-preview",
    "o1-preview-2024-09-12", "o1-mini", "o1-mini-2024-09-12",
    "gpt-4", "open-llm-model",
}

def supports_message_token_count(model: str) -> bool:
    return "claude" in model or model in _MESSAGE_TOKEN_MODELS

assert supports_message_token_count(model)
num_tokens = count_message_tokens(messages, model)

Type guard

from typing import TypeGuard

def is_countable_message_model(model: object) -> TypeGuard[str]:
    return isinstance(model, str) and (
        "claude" in model or model in _MESSAGE_TOKEN_MODELS
    )

if is_countable_message_model(model):
    num_tokens = count_message_tokens(messages, model)

Try / catch

try:
    num_tokens = count_message_tokens(messages, model)
except NotImplementedError as exc:
    fallback = "gpt-4o-mini"  # choose a model with compatible encoding explicitly
    logger.warning("%s; retrying token estimate with %s", exc, fallback)
    num_tokens = count_message_tokens(messages, fallback)

Prevention

When it happens

Trigger: Calling metagpt.utils.token_counter.count_message_tokens(messages, model) directly with a model outside the supported set, such as invalid_model, a typo such as gpt-4o-mini-2024-07-18-typo, or a newer/custom name such as gpt-4.1, o3-mini, qwen..., or deepseek-chat. In OpenAIGPT, _calc_usage() calls it with config.pricing_plan or self.model when calc_usage is true; that call site catches the exception and logs 'usage calculation failed'. OpenAIGPT.count_tokens() calls it with self.model and falls back to the rough BaseLLM heuristic on any exception. get_max_completion_tokens() can also reach it when a model exists in TOKEN_MAX but not in this overhead table.

Common situations: Using an OpenAI-compatible endpoint with a provider-specific model or deployment name and no pricing_plan; adopting a newly released OpenAI model with an older MetaGPT release; Azure deployment names being used where a published model name is expected; typos in config2.yaml llm.model; direct use of token_counter in application code for cost or context-window checks.

Related errors


AI-assisted analysis of FoundationAgents/MetaGPT@11cdf466d0 (2026-08-14). Data as JSON: /api/errors/35eba8350c0059d0. Report an issue: GitHub.

Appendix: source

Thrown at metagpt/actions/action_node.py:262

        return {} if exclude and self.key in exclude else self._get_self_mapping()

    @classmethod
    @register_action_outcls
    def create_model_class(cls, class_name: str, mapping: Dict[str, Tuple[Type, Any]]):
        """基于pydantic v2的模型动态生成,用来检验结果类型正确性"""

        def check_fields(cls, values):
            all_fields = set(mapping.keys())
            required_fields = set()
            for k, v in mapping.items():
                type_v, field_info = v
                if ActionNode.is_optional_type(type_v):
                    continue
                required_fields.add(k)

            missing_fields = required_fields - set(values.keys())
            if missing_fields:
                raise ValueError(f"Missing fields: {missing_fields}")

            unrecognized_fields = set(values.keys()) - all_fields
            if unrecognized_fields:
                logger.warning(f"Unrecognized fields: {unrecognized_fields}")
            return values

        validators = {"check_missing_fields_validator": model_validator(mode="before")(check_fields)}

        new_fields = {}
        for field_name, field_value in mapping.items():
            if isinstance(field_value, dict):
                # 对于嵌套结构,递归创建模型类
                nested_class_name = f"{class_name}_{field_name}"
                nested_class = cls.create_model_class(nested_class_name, field_value)
                new_fields[field_name] = (nested_class, ...)
            else:
                new_fields[field_name] = field_value

View on GitHub (pinned to 11cdf466d0)