test_mode_unsupported
HTTP 422 retryable: false REST and MCP
The request is valid for a live project but the capability it uses has no meaning in test mode, so it is refused rather than approximated.
Why it happens
A test inbox is not a live inbox with sending switched off. It has a non-routable address on .mail.invalid and a simulated transport, and it cannot reach an internet recipient at all. Custom addressing follows from that: a test inbox's address is generated, so supplying a local_part or a domain_id is rejected rather than ignored. Domain attachment and domain verification are refused on test projects for the same reason. There is no DNS to verify and nothing the verified domain could route to.
Every condition below was read in the implementation at the file and line given. They are the branches that raise this code, not a summary of what the name suggests.
- Creating a test inbox while supplying local_part or domain_id. Test addresses are generated and non-routable.
packages/domain/src/services.ts:1342 - Attaching a domain to a project whose environment is not live.
packages/domain/src/custom-domains.ts:203 - Verifying a domain on a project whose environment is not live.
packages/domain/src/custom-domains.ts:367
How to fix it
- Drop local_part and domain_id from the test inbox request and use the generated address. It is returned on the created inbox.
- Create a separate live project for domain work. A project's environment is fixed at creation and there is no conversion.
- Expect the address to end in .mail.invalid. That suffix is reserved precisely so nothing can route to it, which is what makes a test inbox safe.
- For an end-to-end test of a real address, use a live inbox on agents.emailforagents.ai rather than trying to make test mode route.
Why it works this way
Refusing rather than ignoring the custom address fields is the choice here. Accepting local_part and quietly generating something else would leave you holding an address you believe is yours, in an environment where the address is not reachable anyway, and the mistake would only show up when the same code ran against a live project.
Messages you will see
The message field is written for a human reading a log and is not a contract
value. Branch on code instead. These are the exact strings this code is sent
with today, listed so that a search for one of them lands here.
Where it appears
These are the documented surfaces that raise it. The REST base is https://api.emailforagents.ai/v1, and the remote MCP endpoint is https://mcp.emailforagents.ai/mcp.
POST /projects/{project_id}/inboxesPOST /projects/{project_id}/domainsPOST /projects/{project_id}/domains/{domain_id}/verify
The response
Read request_id off the body or the x-request-id header before
doing anything else. It is the only handle on one specific attempt, and for a 503 it is
the only handle at all, because the underlying detail stays server-side.
HTTP/1.1 422
content-type: application/json
x-request-id: req_...
{
"error": {
"code": "test_mode_unsupported",
"message": "Test inboxes use generated non-routable addresses.",
"request_id": "req_...",
"retryable": false
}
}
Verified against packages/domain/src/services.ts:1342. The status comes from ERROR_STATUS in packages/contracts/src/errors.ts, which is the one
place a code is mapped to a status.