# AWS "InvalidClientTokenId" on SES: Causes and Fix

Source: https://wraps.dev/ses/errors/invalid-client-token-id

Why AWS returns InvalidClientTokenId when calling SES, how it differs from an expired token, and how to work out which credential the SDK actually picked up.

## The error

```
The security token included in the request is invalid.
```

AWS exception `InvalidClientTokenId`. The Wraps CLI reports it as `ACCESS_KEY_INVALID`.

## Why this happens

The request never reached SES. AWS could not resolve the access key ID in the signature to a real, active credential. The key was deleted, deactivated, belongs to a different account or partition, or is a temporary key sent without its session token.

-   The access key was deleted or deactivated in IAM.
-   The key belongs to a different AWS account or partition than the endpoint being called.
-   A temporary credential was used without its matching session token.
-   The SDK resolved a different credential than you expected, from a profile or environment variable you forgot was set.

## How to fix it

1.  ### 1. Find out which identity the SDK actually resolved
    
    This answers the question before you change anything. If it fails the same way, the credential chain is picking up something that is not a valid key at all.
    
    ```
    aws sts get-caller-identity
    ```
    
2.  ### 2. Check the key's state in IAM
    
    An access key that has been deactivated still looks correct in a config file. Confirm the key ID exists on the user you think it belongs to, and that it is active.
    
3.  ### 3. Rule out a stale environment variable
    
    AWS\_ACCESS\_KEY\_ID exported in a shell profile silently outranks the profile you passed. Precedence, not correctness, is the usual culprit when the same credentials work elsewhere.
    
4.  ### 4. Reconfigure credentials
    
    Wraps does not rank the ways of providing AWS credentials. SSO, IAM access keys, environment variables and an existing named profile all work. Use whichever your organization already uses.
    
    ```
    aws configure ssoaws sso loginaws configureexport AWS_ACCESS_KEY_ID=<your-key>export AWS_SECRET_ACCESS_KEY=<your-secret>export AWS_PROFILE=<profile-name>
    ```
    

## How to stop it recurring

### Prefer credentials that are not copied around

Long-lived keys pasted into environments are the ones that go stale, get rotated out from under a deploy, or end up pointing at the wrong account.

### Make the resolved identity visible in CI

Logging the caller identity at the start of a deploy turns this class of failure from a mystery into a line of output.

## Common questions

### What is the difference between InvalidClientTokenId and ExpiredToken?

InvalidClientTokenId means AWS cannot resolve the key at all. ExpiredToken means it resolved fine and the session has run out. The first needs a different key, the second needs a refresh.

### Why do the same credentials work in the AWS CLI but not here?

Usually credential precedence. Environment variables outrank the shared config file, so two tools started from different shells can resolve different identities.

### Does InvalidClientTokenId mean my IAM policy is wrong?

No. A policy problem returns AccessDenied, which means AWS knew who you were and refused. This error means AWS never established who you were.

## What Wraps does about this

The Wraps CLI maps InvalidClientTokenId to ACCESS\_KEY\_INVALID and says the key is invalid or deactivated rather than claiming credentials are missing, which is a different problem with a different fix. Wraps stores no AWS credentials. The CLI resolves them from your environment the same way the AWS CLI does.
