FoundationAgents/MetaGPT · error · RuntimeError

use `review` after `fill`

Error message

use `review` after `fill`

What it means

YamlModelWithoutDefault is a Pydantic base class meant to reject configuration files that still contain template placeholders. Its mode='before' model_validator scans the incoming values container for any element containing the substring 'YOUR' and raises ValueError before field validation. In this checkout the class is not used by Config itself (Config inherits YamlModel), so the error comes from a subclass of YamlModelWithoutDefault or equivalent direct validation. With a normal dict, Python iterates keys, so the current check primarily tests top-level field/map keys rather than nested values.

Source

Thrown at metagpt/actions/action_node.py:737

        # generate review comments
        if review_mode == ReviewMode.HUMAN:
            review_comments = await self.human_review()
        else:
            review_comments = await self.auto_review()

        if not review_comments:
            logger.warning("There are no review comments")
        return review_comments

    async def review(self, strgy: str = "simple", review_mode: ReviewMode = ReviewMode.AUTO):
        """only give the review comment of each exist and mismatch key

        :param strgy: simple/complex
         - simple: run only once
         - complex: run each node
        """
        if not hasattr(self, "llm"):
            raise RuntimeError("use `review` after `fill`")
        assert review_mode in ReviewMode
        assert self.instruct_content, 'review only support with `schema != "raw"`'

        if strgy == "simple":
            review_comments = await self.simple_review(review_mode)
        elif strgy == "complex":
            # review each child node one-by-one
            review_comments = {}
            for _, child in self.children.items():
                child_review_comment = await child.simple_review(review_mode)
                review_comments.update(child_review_comment)

        return review_comments

    async def human_revise(self) -> dict[str, str]:
        review_contents = HumanInteraction().interact_with_instruct_content(
            instruct_content=self.instruct_content, mapping=self.get_mapping(mode="auto"), interact_type="revise"
        )

View on GitHub (pinned to 11cdf466d0)

Solutions

  1. Identify the file actually loaded using MetaGPT's priority order, especially ~/.metagpt/config2.yaml versus METAGPT_ROOT/config/config2.yaml, and open that exact file.
  2. Replace every remaining YOUR_* placeholder with a real value; never commit the completed file or paste the secret into chat/logs.
  3. For example/tool blocks you do not use, delete or comment out the entire block instead of leaving YOUR_* values or keys.
  4. In multi-model maps, replace placeholder keys such as YOUR_MODEL_NAME_1 with real model names and provide their api_key/base_url entries.
  5. In CI/Docker, verify the mounted config2.yaml exists at the expected path and contains no template text before starting MetaGPT.

Example fix

# before (~/.metagpt/config2.yaml)
llm:
  api_type: openai
  base_url: https://api.openai.com/v1
  api_key: "YOUR_API_KEY"

# after: put the real key in this local, untracked file
llm:
  api_type: openai
  base_url: https://api.openai.com/v1
  api_key: "sk-REDACTED-REAL-KEY"
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
import yaml

def has_yaml_placeholder(node) -> bool:
    if isinstance(node, str):
        return "YOUR" in node
    if isinstance(node, dict):
        return any("YOUR" in str(k) or has_yaml_placeholder(v) for k, v in node.items())
    if isinstance(node, list):
        return any(has_yaml_placeholder(item) for item in node)
    return False

path = Path.home() / ".metagpt/config2.yaml"
raw = yaml.safe_load(path.read_text(encoding="utf-8"))
if has_yaml_placeholder(raw):
    raise RuntimeError(f"Replace every YOUR_* placeholder in {path} before loading it")
model = YourYamlModelWithoutDefault.from_yaml_file(path)

Type guard

from typing import Any, TypeGuard

def is_placeholder_free_config(data: object) -> TypeGuard[dict[str, Any]]:
    return isinstance(data, dict) and not has_yaml_placeholder(data)

raw = yaml.safe_load(path.read_text(encoding="utf-8"))
if not is_placeholder_free_config(raw):
    raise RuntimeError("config2.yaml still contains YOUR_* placeholders")

Try / catch

from pydantic import ValidationError

try:
    model = YourYamlModelWithoutDefault.model_validate(raw)
except ValidationError as exc:
    if "Please set your config in config2.yaml" in str(exc):
        raise RuntimeError("Unconfigured MetaGPT config: replace all YOUR_* placeholders") from exc
    raise

Prevention

When it happens

Trigger: Calling YourYamlModelWithoutDefault.from_yaml_file(path), YourYamlModelWithoutDefault.model_validate(data), or its constructor with input containing a top-level element whose text contains 'YOUR', such as a key named YOUR_MODEL_NAME_1. It is intended to trigger when config2.yaml or a copied example still has placeholders such as YOUR_API_KEY, YOUR_BASE_URL, or YOUR_PROXY; a nested api_key: YOUR_API_KEY is what users usually intend it to catch, though the implementation's top-level scan can miss it depending on the input shape. Pydantic normally wraps the raised ValueError in a ValidationError.

Common situations: Copying config/config2.example.yaml or running metagpt --init-config and never replacing the YOUR_* placeholders; editing the repository config/config2.yaml while MetaGPT actually loads ~/.metagpt/config2.yaml; CI or Docker runs where the intended config2.yaml secret was not mounted; copied multi-model examples that leave YOUR_MODEL_NAME_1/YOUR_MODEL_NAME_2 keys in place; retaining unused example tool sections filled with placeholders.

Related errors


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