Agent readiness, approvals, and timeouts
Agent test requires --harness
pytest stops during collection with agent test requires --harness or m3(agents=[...]) when a test requests the agent fixture and nothing selects a harness for it. Select one in either place:
- on the command line:
m3 test --harness KIND=MODEL - on the test:
pytest.mark.m3(agents=[...])
A test can also skip the fixture and select its agent with kit.agents([...]), as the first agent test does. That test runs under plain pytest with no flags.
-k does not avoid the error, because collection fails before deselection. Keep agent tests in their own file so that direct tests can run by path without a harness. See the pytest plugin reference.
Harness executable is unavailable
Use m3 doctor --require harness:NAME for a system runtime, or select a managed runtime/version supported on the current OS and CPU.
Codex has no login or rejects the credentials
Codex needs either an API key or a ChatGPT subscription login. Startup fails with Codex has no login when it has neither: map a key with --credential-env codex:OPENAI_API_KEY=SOURCE, or sign in to Codex on the host and run without a mapped key. When a turn fails with Codex turn failed: model provider rejected the credentials (HTTP 401), the mapped key or the copied login was refused by the model provider. See credentials.
A failed agent turn ends the execution with the harness's own reason in result.error.message and result.error.details["cause"]. Codex failures add reason and http_status to the details. session lost during turn means the harness gave no reason. A turn that timed out, was cancelled, or was interrupted ends the execution with that same outcome rather than failed.
Tool call was denied
Approval is separate from MCP elicitation. The test's tool selection decides which tools on its bound servers the agent may call: omitting tools allows every tool the server advertises, tools=[...] allows only the named ones, and tools=[] allows none.
Codex asks for approval before each MCP tool call. M3 answers it from that tool selection, so a Codex test needs no permission setting; a tool outside the selection is declined even if permission_policy="allow" is set. If Codex lists tools but makes no call, check the tools selection and the prompt.
Codex also asks before running a command outside its sandbox, editing files outside the workspace, or widening sandbox permissions (for example network access). M3 answers these from permission_policy, so they are declined by default and the model carries on without them. The trace records each one as a permission.request/permission.response pair. Pass permission_policy="allow" only for a trusted agent and a scoped workspace.
ACP agents are different: every permission request they send, including one for an MCP tool call, goes to permission_policy, which denies by default. For an ACP agent that asks before calling tools, pass permission_policy="allow" to agent.run(...) or agent.session(...). Allow it only for a trusted agent and a scoped workspace, because the same setting also approves the agent's requests to run commands or edit files.
Execution timed out
Each agent execution gets 180 seconds unless you change it. --execution-timeout SECONDS changes the deadline for every selected agent, but only where the test doesn't pass its own timeout=. A timeout= in agent.run(), agent.submit(), or agent.session() always wins, so raising the flag does nothing for those calls. Change the value in the test instead.
Inspect the trace and harness diagnostics before increasing either value; a server waiting for input or approval will not be fixed by an arbitrary larger value.
A timed-out trace keeps the messages, tool calls, and usage that Codex, Pi, Claude Code, and ACP agents reported before the deadline, with the capture_incomplete limitation. An agent that was still working shows that activity; one that stalled shows none. OpenCode returns a turn in one response, so its timed-out turns have no messages or tool calls.
An explicit tool request that times out while waiting for a harness response may be an unanswered Codex MCP approval request. agent.run(..., timeout=...) covers startup, turns, and cleanup; handle.result(timeout=...) limits only waiting. Diagnose with trace diagnostics: stage, operation, elapsed time, and configured seconds.