{"record":{"id":"35eba8350c0059d0","repo":"FoundationAgents/MetaGPT","slug":"missing-fields-missing-fields","errorCode":null,"errorMessage":"Missing fields: {missing_fields}","messagePattern":"Missing fields: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"metagpt/actions/action_node.py","lineNumber":262,"sourceCode":"        return {} if exclude and self.key in exclude else self._get_self_mapping()\n\n    @classmethod\n    @register_action_outcls\n    def create_model_class(cls, class_name: str, mapping: Dict[str, Tuple[Type, Any]]):\n        \"\"\"基于pydantic v2的模型动态生成，用来检验结果类型正确性\"\"\"\n\n        def check_fields(cls, values):\n            all_fields = set(mapping.keys())\n            required_fields = set()\n            for k, v in mapping.items():\n                type_v, field_info = v\n                if ActionNode.is_optional_type(type_v):\n                    continue\n                required_fields.add(k)\n\n            missing_fields = required_fields - set(values.keys())\n            if missing_fields:\n                raise ValueError(f\"Missing fields: {missing_fields}\")\n\n            unrecognized_fields = set(values.keys()) - all_fields\n            if unrecognized_fields:\n                logger.warning(f\"Unrecognized fields: {unrecognized_fields}\")\n            return values\n\n        validators = {\"check_missing_fields_validator\": model_validator(mode=\"before\")(check_fields)}\n\n        new_fields = {}\n        for field_name, field_value in mapping.items():\n            if isinstance(field_value, dict):\n                # 对于嵌套结构，递归创建模型类\n                nested_class_name = f\"{class_name}_{field_name}\"\n                nested_class = cls.create_model_class(nested_class_name, field_value)\n                new_fields[field_name] = (nested_class, ...)\n            else:\n                new_fields[field_name] = field_value\n","sourceCodeStart":244,"sourceCodeEnd":280,"githubUrl":"https://github.com/FoundationAgents/MetaGPT/blob/11cdf466d042aece04fc6cfd13b28e1a70341b1f/metagpt/actions/action_node.py#L244-L280","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","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.","Upgrade MetaGPT/token_counter.py to a release whose model list includes your model, or patch the local set if you maintain a fork.","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.","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."],"exampleFix":"# before (config2.yaml)\nllm:\n  api_type: openai\n  base_url: https://your-openai-compatible-endpoint/v1\n  model: my-qwen-deployment\n\n# after: keep the served model, but count usage with a supported pricing plan\nllm:\n  api_type: openai\n  base_url: https://your-openai-compatible-endpoint/v1\n  model: my-qwen-deployment\n  pricing_plan: gpt-4o-mini","handlingStrategy":"validation","validationCode":"from metagpt.utils.token_counter import count_message_tokens\n\n_MESSAGE_TOKEN_MODELS = {\n    \"gpt-3.5-turbo-0613\", \"gpt-3.5-turbo-16k-0613\", \"gpt-35-turbo\",\n    \"gpt-35-turbo-16k\", \"gpt-3.5-turbo-16k\", \"gpt-3.5-turbo-1106\",\n    \"gpt-3.5-turbo-0125\", \"gpt-3.5-turbo-0301\", \"gpt-3.5-turbo\",\n    \"gpt-4-0314\", \"gpt-4-32k-0314\", \"gpt-4-0613\", \"gpt-4-32k-0613\",\n    \"gpt-4-turbo\", \"gpt-4-turbo-preview\", \"gpt-4-0125-preview\",\n    \"gpt-4-1106-preview\", \"gpt-4-vision-preview\", \"gpt-4-1106-vision-preview\",\n    \"gpt-4o\", \"gpt-4o-2024-05-13\", \"gpt-4o-2024-08-06\",\n    \"gpt-4o-mini\", \"gpt-4o-mini-2024-07-18\", \"o1-preview\",\n    \"o1-preview-2024-09-12\", \"o1-mini\", \"o1-mini-2024-09-12\",\n    \"gpt-4\", \"open-llm-model\",\n}\n\ndef supports_message_token_count(model: str) -> bool:\n    return \"claude\" in model or model in _MESSAGE_TOKEN_MODELS\n\nassert supports_message_token_count(model)\nnum_tokens = count_message_tokens(messages, model)","typeGuard":"from typing import TypeGuard\n\ndef is_countable_message_model(model: object) -> TypeGuard[str]:\n    return isinstance(model, str) and (\n        \"claude\" in model or model in _MESSAGE_TOKEN_MODELS\n    )\n\nif is_countable_message_model(model):\n    num_tokens = count_message_tokens(messages, model)","tryCatchPattern":"try:\n    num_tokens = count_message_tokens(messages, model)\nexcept NotImplementedError as exc:\n    fallback = \"gpt-4o-mini\"  # choose a model with compatible encoding explicitly\n    logger.warning(\"%s; retrying token estimate with %s\", exc, fallback)\n    num_tokens = count_message_tokens(messages, fallback)","preventionTips":["Never pass provider deployment IDs or arbitrary custom names to count_message_tokens; map them to a supported published model with llm.pricing_plan.","Validate the model against a local copy of the supported set before calling the counter or running jobs that calculate usage.","Treat 'usage calculation failed: num_tokens_from_messages()...' as a configuration defect, not a transient API failure.","Pin a known supported model version in automated tests instead of a mutable alias or newest model string.","When adding a new model to your fork, add both the overhead table entry and TOKEN_MAX/pricing data in the same change."],"tags":["tokens","tiktoken","model-config","python","openai"],"backgroundTag":null,"analyzedSha":"11cdf466d042aece04fc6cfd13b28e1a70341b1f","analyzedAt":"2026-08-14T23:20:02.994Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}