FoundationAgents/MetaGPT · error · ValueError
`req` must be provided as a keyword argument.
Error message
`req` must be provided as a keyword argument.
What it means
The exp_pool @enable_exp_pool decorator requires the wrapped function's request to arrive as a keyword argument named exactly `req`. _validate_params checks `"req" in self.kwargs` and raises ValueError otherwise, because the pool serializes and retrieves experiences keyed on that kwarg. Passing req positionally, or naming it differently, fails validation before the function runs.
Solutions
- Call the decorated function with the keyword: await obj.run(req=ExperienceRequest(...)) (or whatever request object the function expects).
- Rename the function's parameter to req if you control the definition, so positional-style keyword calls line up.
- If you cannot change callers, wrap the function: define an adapter that accepts your name and forwards req=... to the decorated target.
Example fix
# before
@enable_exp_pool
class _Run:
async def run(self, req): ...
await runner.run("some question") # ValueError: `req` must be provided as a keyword argument.
# after
await runner.run(req="some question") Defensive patterns
Strategy: validation
Validate before calling
# the decorator requires this exact keyword; check your call site before invoking assert "req" in kwargs_of_call, "decorated function must be called with req=... as a keyword argument" await obj.run(req=req_obj)
Try / catch
try:
await runner.run(req=q)
except ValueError as e:
if "`req` must be provided" in str(e):
raise TypeError("call exp-pool-decorated functions with req=<request> keyword") from e
raise Prevention
- Adopt the convention `req` everywhere: decorated function parameters and call sites use the same name.
- Add a unit test that calls each decorated function once — the ValueError surfaces immediately in CI, not in production.
- When wrapping third-party functions with @enable_exp_pool, write a thin adapter that forwards req=...
When it happens
Trigger: Decorating a method and calling it as obj.run(query) (positional) or obj.run(request=query) instead of obj.run(req=query); the decorator applies to both sync and async functions, both wrappers enforce the contract.
Common situations: Adding the decorator to an existing method whose parameters use a different name; refactoring call sites and dropping the `req=` keyword; copying examples that predate the keyword-argument requirement.
Related errors
- To use the experience pool, you need to install the rag…
- api_name: not found
- create device path: failed
- Data type not supported for metadata extraction.
- Delete collection failed.
AI-assisted analysis of FoundationAgents/MetaGPT@11cdf466d0 (2026-08-14).
Data as JSON: /api/errors/7565b7d5ea2b6f7e.
Report an issue: GitHub.
Appendix: source
Thrown at metagpt/exp_pool/decorator.py:197
self.exp_manager.create_exp(exp)
self._log_exp(exp)
@staticmethod
def choose_wrapper(func, wrapped_func):
"""Choose how to run wrapped_func based on whether the function is asynchronous."""
async def async_wrapper(*args, **kwargs):
return await wrapped_func(args, kwargs)
def sync_wrapper(*args, **kwargs):
NestAsyncio.apply_once()
return asyncio.get_event_loop().run_until_complete(wrapped_func(args, kwargs))
return async_wrapper if asyncio.iscoroutinefunction(func) else sync_wrapper
def _validate_params(self):
if "req" not in self.kwargs:
raise ValueError("`req` must be provided as a keyword argument.")
def _generate_tag(self) -> str:
"""Generates a tag for the self.func.
"ClassName.method_name" if the first argument is a class instance, otherwise just "function_name".
"""
if self.args and hasattr(self.args[0], "__class__"):
cls_name = type(self.args[0]).__name__
return f"{cls_name}.{self.func.__name__}"
return self.func.__name__
async def _build_context(self) -> str:
self.context_builder.exps = self._exps
return await self.context_builder.build(self.kwargs["req"])
View on GitHub (pinned to 11cdf466d0)