FoundationAgents/MetaGPT · error · ValueError

{rw_api} not exists

Error message

{rw_api} not exists

What it means

ExtEnv._check_api_exist raises ValueError('{rw_api} not exists') when the argument is falsy — None or an empty string. Despite the message showing the value, the only failing condition is `not rw_api`; a non-empty string always passes this check (existence in the registry is enforced separately by EnvAPIRegistry.get). It is a guard against calling read/write APIs without specifying which one.

Source

Thrown at metagpt/environment/base_env.py:63


def mark_as_writeable(func):
    """mark functionn as a writeable one in ExtEnv, it does something to ExtEnv"""
    env_write_api_registry[func.__name__] = get_function_schema(func)
    return func


class ExtEnv(BaseEnvironment, BaseModel):
    """External Env to integrate actual game environment"""

    model_config = ConfigDict(arbitrary_types_allowed=True)

    action_space: spaces.Space[ActType] = Field(default_factory=spaces.Space, exclude=True)
    observation_space: spaces.Space[ObsType] = Field(default_factory=spaces.Space, exclude=True)

    def _check_api_exist(self, rw_api: Optional[str] = None):
        if not rw_api:
            raise ValueError(f"{rw_api} not exists")

    def get_all_available_apis(self, mode: str = "read") -> list[Any]:
        """get available read/write apis definition"""
        assert mode in ["read", "write"]
        if mode == "read":
            return env_read_api_registry.get_apis()
        else:
            return env_write_api_registry.get_apis()

    async def read_from_api(self, env_action: Union[str, EnvAPIAbstract]):
        """get observation from particular api of ExtEnv"""
        if isinstance(env_action, str):
            env_read_api = env_read_api_registry.get(api_name=env_action)["func"]
            self._check_api_exist(env_read_api)
            if is_coroutine_func(env_read_api):
                res = await env_read_api(self)
            else:
                res = env_read_api(self)

View on GitHub (pinned to 11cdf466d0)

Solutions

  1. Always pass a concrete api name string: await env.read_from_api("api_name") or a fully-populated EnvAPIAbstract.
  2. If building action objects dynamically, assert api_name is non-empty before calling the env.
  3. When accepting api names from config/user input, validate non-empty at the boundary with a clear error.

Example fix

# before
obs = await env.read_from_api("")  # ValueError:  not exists

# after
api_name = "get_map"  
assert api_name, "api name must be a non-empty string"
obs = await env.read_from_api(api_name)
Defensive patterns

Strategy: validation

Validate before calling

if not rw_api or not isinstance(rw_api, str):
    raise ValueError("an api name (non-empty str) is required before calling read/write APIs")

Type guard

def is_valid_api_name(name) -> bool:
    """True when name passes ExtEnv._check_api_exist's falsy check."""
    return isinstance(name, str) and bool(name)

Try / catch

try:
    await env.read_from_api(api_name)
except ValueError as e:
    if "not exists" in str(e):
        raise ValueError("api name was None/empty; populate api_name before calling") from e
    raise

Prevention

When it happens

Trigger: await env.read_from_api(None) or env.read_from_api("") — e.g. an EnvAPIAbstract whose api_name field defaulted to "", or a variable that failed to populate and defaulted to None.

Common situations: Constructing EnvAPIAbstract subclasses without setting api_name (it defaults to ""); passing a programmatically-derived api name that resolved to None from a failed lookup; refactoring call sites to pass the api name positionally where it got dropped.

Related errors


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