Common errors
Objective
Section titled “Objective”An error response is part of the contract. Callers branch on error codes, so a code whose meaning changes breaks working software as surely as a removed endpoint.
Read this guide to handle errors correctly, and to know which are worth retrying.
Requirements
Section titled “Requirements”- None
Instructions
Section titled “Instructions”The error envelope
Section titled “The error envelope”Errors are returned as XML in the Amazon EC2 shape:
<?xml version="1.0" encoding="UTF-8"?><Response> <Errors> <Error> <Code>InvalidInstanceID.NotFound</Code> <Message>The instance ID 'i-0a1b2c3d4e5f60718' does not exist</Message> </Error> </Errors> <RequestID>b1e2c3d4-5678-90ab-cdef-1234567890ab</RequestID></Response>Note the element names: <Response>, <Errors>, <Error>, and <RequestID>
with that exact casing. There is no <Type> element; that belongs to the
standard query protocol used by IAM, not to EC2. A client parsing EC2 errors
expects this shape and no other.
The <Code> is stable and is what you branch on. The <Message> is for a
human and its wording may change between releases. Never parse the message.
Status codes
Section titled “Status codes”| Status | Meaning |
|---|---|
400 | The request was malformed, invalid, or expired |
403 | The credentials are unknown, or policy denies the action |
404 | Reserved; EC2 signals absence with a .NotFound code and a 400 |
409 | The request conflicts with the state of the resource |
500 | An error on our side |
503 | Temporarily unavailable, or the request rate was exceeded |
We return no 401. Amazon EC2’s own common-error list does document
NotAuthorized at 401, and real EC2 returns 401 for some AuthFailure cases —
so this is a deliberate divergence rather than parity, and it is stated as one.
We return 403 for both. Two reasons: the AWS SDKs’ automatic clock-skew
correction is keyed on a 403 or 400 carrying a recognised code, and a 401
without a WWW-Authenticate header violates HTTP and confuses intermediaries
that special-case it.
OPEN (Michael): confirm the 403 choice against the SDK clock-skew handlers before shipping, since that behaviour is the stated reason for the whole table.
Client errors
Section titled “Client errors”| Code | Status | Cause | Retry |
|---|---|---|---|
AuthFailure | 403 | The credentials could not be validated | No |
IncompleteSignature | 400 | The Authorization header is malformed | No |
SignatureDoesNotMatch | 403 | The signature does not match the request | No |
InvalidClientTokenId | 403 | The access key id does not exist or is inactive | No |
MissingAuthenticationToken | 403 | The request was not signed | No |
RequestExpired | 400 | The timestamp is outside the permitted skew | After fixing the clock |
UnauthorizedOperation | 403 | Policy does not allow this action | No |
InvalidAction | 400 | The action does not exist in this version | No |
InvalidParameterValue | 400 | A parameter has an unacceptable value | No |
InvalidParameterCombination | 400 | Two parameters cannot be used together | No |
MissingParameter | 400 | A required parameter is absent | No |
InvalidPaginationToken | 400 | The token is invalid or has expired | No |
IdempotentParameterMismatch | 400 | A client token was reused with different parameters | No |
InvalidID | 400 | An identifier is malformed | No |
*.NotFound | 400 | The named resource does not exist | Only if just created |
*.Duplicate | 400 | A resource with that name already exists | No |
*.InUse | 400 | The resource is in use and cannot be changed | No |
DependencyViolation | 400 | Another resource depends on this one | No |
InstanceLimitExceeded | 400 | An account quota would be exceeded | No |
InsufficientInstanceCapacity | 500 | No capacity for that type right now | Yes, with backoff |
DryRunOperation | 400 | DryRun was set; the call would have succeeded | No |
Blocked | 403 | The account is suspended | No |
MalformedQueryString | 404 | The query string is not well-formed | No |
InvalidParameterCombination is returned, among other cases, when a describe
call is given both a list of ids and MaxResults. That combination is rejected
by Amazon EC2 and is rejected here.
Server errors
Section titled “Server errors”| Code | Status | Cause | Retry |
|---|---|---|---|
InternalError | 500 | An unexpected failure on our side | Yes |
Unavailable | 503 | The service is temporarily unavailable | Yes |
RequestLimitExceeded | 503 | The request rate was exceeded | Yes, with backoff |
Throttling is RequestLimitExceeded with a 503, which is what Amazon EC2
returns and what the Terraform AWS provider matches on for its backoff. A 429
would not be recognised by the installed base of older clients.
Absence, denial, and what an error must not reveal
Section titled “Absence, denial, and what an error must not reveal”A resource belonging to another account returns the same .NotFound as a
resource that has never existed. Existence is not confirmed to a caller who is
not entitled to know it.
Within an account, a resource that exists but that policy forbids returns
UnauthorizedOperation. The rule is therefore:
- another account, or no such resource →
.NotFound - this account, denied by policy →
UnauthorizedOperation
No error contains internal detail: no stack traces, no hostnames, no query text.
Deprecating a code
Section titled “Deprecating a code”An error code is never given a new meaning. A behaviour needing a different meaning gets a new code, and the old one keeps returning what it always did until it is removed with notice.