Error Codes & Troubleshooting
Complete reference for all CLI error codes and SDK error classes, with solutions for each.
Complete reference for all CLI error codes and SDK error classes, with solutions for each.
| Code | Message | Solution |
|---|---|---|
NO_AWS_CREDENTIALS | AWS credentials not found | Set up AWS SSO (aws configure sso), IAM access keys (aws configure), environment variables, or AWS_PROFILE |
PROFILE_NOT_FOUND | AWS profile "<profile>" not found | List available profiles, or configure a new one: aws configure --profile <profile> |
CREDENTIALS_FILE_MISSING | AWS credentials file not found | Set up AWS SSO, IAM access keys, environment variables, or AWS_PROFILE |
ACCESS_KEY_INVALID | AWS access key is invalid or has been deactivated | Check the IAM console, run aws configure, or generate new access keys |
SESSION_TOKEN_EXPIRED | AWS session token has expired | SSO users: aws sso login. Assumed roles: re-run your assume-role command |
SSO_SESSION_EXPIRED | AWS SSO session has expired for profile "<profile>" | Run aws sso login --profile <profile> |
NOT_AUTHENTICATED | Not authenticated to Wraps Platform | Run wraps auth login, or provide --token / WRAPS_API_KEY |
ORG_NOT_FOUND | Could not determine organization | Pass --org, or sign in first: wraps auth login |
| Code | Message | Solution |
|---|---|---|
IAM_PERMISSION_DENIED | Permission denied: <action> on <resource> | Your credentials lack the permission. View required permissions: wraps permissions --json |
SES_PERMISSION_DENIED | SES permission denied: <action> | wraps permissions --service email --json |
DYNAMODB_PERMISSION_DENIED | DynamoDB permission denied | Needs CreateTable, DeleteTable, DescribeTable, UpdateTable |
LAMBDA_PERMISSION_DENIED | Lambda permission denied | Needs CreateFunction, UpdateFunctionCode, DeleteFunction |
EVENTBRIDGE_PERMISSION_DENIED | EventBridge permission denied | Needs PutRule, PutTargets, DeleteRule |
SQS_PERMISSION_DENIED | SQS permission denied | Needs CreateQueue, DeleteQueue, GetQueueAttributes |
CLOUDWATCH_LOGS_PERMISSION_DENIED | CloudWatch Logs permission denied | Needs logs:DescribeLogGroups, logs:FilterLogEvents, logs:StartLiveTail |
ROUTE53_PERMISSION_DENIED | Route53 permission denied | Needs ChangeResourceRecordSets, ListHostedZones (optional — add DNS records manually instead) |
IAM_ENTITY_NOT_FOUND | IAM entity not found | Run wraps platform connect |
| Code | Message | Solution |
|---|---|---|
INVALID_REGION | Invalid AWS region: <region> | Use a valid AWS region like us-east-1, eu-west-1, ap-southeast-1 |
REGION_REQUIRED | Region is required and could not be determined | Pass --region or set AWS_REGION |
REGION_REQUIRED_FOR_SET | Could not determine which Region to change | Pass --region <r> with the Region you want to change |
AWS_THROTTLED | AWS request was throttled | Wait and retry. Request a quota increase if this happens repeatedly |
AWS_LIMIT_EXCEEDED | AWS service limit exceeded | Request a quota increase in Service Quotas |
| Code | Message | Solution |
|---|---|---|
NO_STACK | No Wraps infrastructure found in this AWS account | Run wraps email init |
STACK_EXISTS | Stack "<stackName>" already exists | To update: wraps email upgrade. To remove: wraps destroy --stack <stackName> |
STACK_LOCKED | The Pulumi stack is locked from a previous run | Remove ~/.wraps/pulumi/.pulumi/locks (local), or delete the lock object under .pulumi/locks/ in your wraps-state-* bucket (S3) |
PULUMI_ERROR | Infrastructure deployment failed: <message> | Check your AWS permissions and try again |
PULUMI_NOT_INSTALLED | Pulumi CLI is not installed | Install via brew/curl/choco, or download from pulumi.com/docs/install |
RESOURCE_CONFLICT | Resource already exists: <resourceName> | Diagnose and clean up: wraps email doctor --cleanup |
S3_STATE_BUCKET_CREATION_FAILED | Failed to create S3 state bucket: <bucketName> | Ensure s3:CreateBucket, s3:PutBucketEncryption, s3:PutBucketVersioning, or export WRAPS_LOCAL_ONLY=1 |
S3_STATE_ACCESS_DENIED | Access denied to S3 state bucket | Ensure s3:GetObject, s3:PutObject, s3:ListBucket on wraps-state-*, or export WRAPS_LOCAL_ONLY=1 |
STATE_MIGRATION_FAILED | Failed to migrate Pulumi state to S3 | Local state is still intact. export WRAPS_LOCAL_ONLY=1 to skip migration |
SELFHOST_NO_LOG_GROUPS | No self-hosted log groups found in <region> | Check wraps selfhost status, or target a different region |
SELFHOST_NO_LOG_GROUPS_FOR_SOURCE | No <source> log groups in this deployment | Drop the filter: wraps selfhost logs |
LAMBDA_FUNCTION_NOT_FOUND | Agent enforcer Lambda not found | Deploy it: wraps email agent create |
DYNAMODB_TABLE_NOT_FOUND | Agent policy table not found | Deploy it: wraps email agent create |
| Code | Message | Solution |
|---|---|---|
SES_MESSAGE_REJECTED | SES rejected the message: <detail> | Sandbox with unverified recipient, unverified sender identity, or receiving-only domain. Check wraps email status / wraps email doctor |
SES_MAIL_FROM_NOT_VERIFIED | SES MAIL FROM domain is not verified: <detail> | Check DNS records: wraps email verify |
SES_ACCOUNT_SENDING_PAUSED | SES account-level sending is paused | Check the SES console Reputation Dashboard |
SES_CONFIG_SET_SENDING_PAUSED | SES configuration set sending is paused | Resume it in the SES console, or send without that configuration set |
SES_CONFIG_SET_MISSING | SES configuration set does not exist: <detail> | Create it in the SES console, switch regions, or remove ConfigurationSetName |
EVENT_DESTINATION_NOT_FOUND | Event destination not found for <domain> | Run wraps email upgrade |
INVALID_EVENT_DESTINATION | Event destination for <domain> is not an EventBridge destination | Run wraps email upgrade |
EVENT_TYPES_MISSING_SUPPRESSION_EVENTS | eventTracking.events is missing required event type(s): <missing> | BOUNCE and COMPLAINT must always be included in eventTracking.events |
INVALID_SES_PRICING_PLAN | No pricing plan specified | Pass --set with one of the valid pricing plans |
SES_PRICING_PLAN_CHANGE_REJECTED | SES rejected the pricing plan change: <detail> | Check the current plan: wraps email plan |
| Code | Message | Solution |
|---|---|---|
INBOUND_REGION_NOT_SUPPORTED | SES email receiving is not supported in <region> | Deploy in us-east-1, us-west-2, or eu-west-1 |
INBOUND_REQUIRES_OUTBOUND | Inbound email requires outbound email infrastructure | Run wraps email init, then wraps email inbound init |
RECEIPT_RULE_SET_CONFLICT | Another receipt rule set is active: <activeRuleSet> | Wraps will activate wraps-inbound-rules, deactivating the current set |
INBOUND_TEST_SEND_FAILED | Failed to send inbound test email to <recipient> | Check wraps email status / wraps email doctor |
INBOUND_TEST_MAIL_FROM_NOT_VERIFIED | Custom MAIL FROM domain is not verified for <domain> | Verify DNS records: wraps email verify |
INBOUND_TEST_MESSAGE_REJECTED | SES rejected the inbound test send: <message> | Sandbox with unverified recipient, unverified sender domain, or receiving-only domain. Check wraps email status / doctor |
INBOUND_TEST_SENDING_PAUSED | SES sending is paused for this account | Check the SES console Reputation Dashboard |
INBOUND_TEST_PERMISSION_DENIED | IAM permission denied: ses:SendEmail in <region> | wraps permissions --service email --json |
| Code | Message | Solution |
|---|---|---|
REPLY_SECRET_PARAMETER_MISSING | SSM parameter for <domain> was not created | Run wraps email reply status to diagnose, or retry the init |
REPLY_REQUIRES_INBOUND | Reply threading requires inbound email infrastructure | Deploy inbound first: wraps email inbound init |
REPLY_NO_INBOUND_DOMAINS | No inbound domains configured | Add one: wraps email inbound add --domain yourapp.com |
REPLY_INBOUND_DOMAIN_NOT_FOUND | Domain <target> is not configured for inbound email | Add it to inbound first: wraps email inbound add <target> |
REPLY_ALREADY_ENABLED | Reply threading is already enabled for <target> | To rotate the signing secret: wraps email reply rotate --domain <target> |
REPLY_MISSING_DOMAIN | Specify a domain or use --all | wraps email reply init --domain yourapp.com, or --all |
REPLY_ROTATE_MISSING_DOMAIN | --domain is required for rotate | wraps email reply rotate --domain yourapp.com |
REPLY_NOT_ENABLED | Reply threading is not enabled | Enable it first: wraps email reply init --domain yourapp.com |
REPLY_DOMAIN_NOT_ENABLED | Reply threading is not enabled for <domain> | Enable it first: wraps email reply init --domain <domain> |
REPLY_DESTROY_MISSING_DOMAIN | Specify a domain or use --all | wraps email reply destroy --domain yourapp.com, or --all |
REPLY_DECODE_MISSING_ADDRESS | Usage: wraps email reply decode <token>@r.mail.yourapp.com | Provide a signed reply address |
REPLY_DECODE_MALFORMED_ADDRESS | Address must be in the form <token>@r.mail.example.com | Pass a full signed reply address |
| Code | Message | Solution |
|---|---|---|
SMS_NOT_CONFIGURED | SMS infrastructure not found | Run wraps sms init |
SMS_PHONE_NOT_VERIFIED | Phone number registration not complete | Toll-free numbers require registration (15+ days). Check status in the AWS console |
SMS_OPTED_OUT | Destination number <phoneNumber> has opted out | The recipient can opt back in by texting START |
SMS_SPENDING_LIMIT | AWS SMS spending limit reached | Request a spending limit increase in the AWS console |
SMS_INVALID_PHONE_NUMBER | Invalid phone number format: <phoneNumber> | Use E.164 format, e.g. +14155551234 |
SMS_INVALID_COUNTRIES | Invalid --countries value: <raw> | Comma-separated ISO 3166-1 alpha-2 codes, e.g. --countries US,CA,GB |
SMS_INVALID_VOLUME | Invalid --volume value: <raw> | Positive whole number of messages per month |
SMS_SIMULATOR_LIMIT | Simulator daily message limit reached (100 messages) | Upgrade: wraps sms upgrade --phone-type toll-free |
| Code | Message | Solution |
|---|---|---|
SMTP_CREDENTIALS_NOT_FOUND | SMTP credentials not found | wraps email upgrade and select "Enable SMTP credentials" |
SMTP_REQUIRES_SENDING | SMTP credentials require email sending to be enabled | wraps email upgrade and select "Custom configuration" |
| Code | Message | Solution |
|---|---|---|
WRAPS_CONFIG_NOT_FOUND | wraps/wraps.config.ts not found | Initialize templates first: wraps email templates init |
TEMPLATE_COMPILATION_FAILED | Failed to compile template "<name>": <error> | Check your template for syntax errors and valid imports |
TEMPLATE_PUSH_FAILED | Failed to push template "<name>": <error> | Check your API key and network connection |
TEMPLATES_DIR_EXISTS | wraps/ directory already exists | Use --force to overwrite: wraps email templates init --force |
| Code | Message | Solution |
|---|---|---|
NON_INTERACTIVE_INPUT | <what> is required in non-interactive mode | Pass the required flag, or run in an interactive terminal |
UNKNOWN_COMMAND | Unknown <command kind>: <what was typed> | Run wraps --help, or use one of the commands listed in the error's suggestion |
MISSING_REQUIRED_FLAG | A required flag is missing in JSON mode | Provide the flag named in the error message |
JSON_REQUIRES_FORCE | --force flag is required in JSON mode for destructive operations | Add --force to the command |
CONFIRMATION_REQUIRED | Confirmation required to change your SES pricing plan | Pass --yes to skip the confirmation prompt |
OPERATION_CANCELLED | Operation cancelled | Pass --region to skip the interactive prompt |
INVALID_LOG_SOURCE | Unknown --source value: <value> | Valid sources: api, web, workers, other, all |
INVALID_LOG_WINDOW | Invalid --since value: <value> | Use a positive number followed by s, m, h, or d |
UNKNOWN_ERROR | An unexpected error occurred | The thrown value was not a recognized WrapsError. Check the CLI logs for details |
Every command exits 0 on success and 1 on error, with one exception.
| Command | Exit code | Meaning |
|---|---|---|
| Any command | 0 | Success |
| Any command | 1 | Error |
wraps email check | 0 | Deliverability grade A or B |
wraps email check | 1 | Deliverability grade C or D |
wraps email check | 2 | Deliverability grade F |
wraps email check | 4 | Check itself failed to run, or grade was unrecognized |
In --json mode, errors are still written to stdout as a JSON envelope — the exit code is what signals failure to scripts:
{ "success": false, "command": "email.init", "error": { "code": "<one of the CLI error codes below>", "message": "...", "suggestion": "...", "docsUrl": "https://wraps.dev/docs/..." }}@wraps.dev/email)Base class every other email SDK error extends. Use it for a single catch-all instanceof check that covers all of them.
Thrown when an AWS SES API call fails.
| Property | Type | Description |
|---|---|---|
code | string | MessageRejected, Throttling, AccountSuspended, MailFromDomainNotVerified |
requestId | string | AWS request identifier |
retryable | boolean | Whether the request can be retried |
Thrown when an email history read/write operation fails.
| Property | Type | Description |
|---|---|---|
code | string | DynamoDB error code |
requestId | string | AWS request identifier |
retryable | boolean | Whether the request can be retried |
Thrown when input parameters are invalid.
| Property | Type | Description |
|---|---|---|
field | string | Which field failed validation |
message | string | Human-readable error description |
The AWS credential chain produced nothing usable, so no request was ever signed. The message lists every way to supply credentials — SSO, access keys, environment variables, a named profile, or the constructor — without ranking one over another.
| Property | Type | Description |
|---|---|---|
cause | unknown | The underlying AWS SDK error, for debugging |
Extends SESError. SES rejected the send because an identity involved is not verified in the region the request went to. Two unrelated causes produce this one AWS error — a region mismatch and the SES sandbox — so the message names the region actually used and walks through both, including the mailbox-simulator address that proves sending works without production access.
| Property | Type | Description |
|---|---|---|
region | string | undefined | The region this client sent to, when resolvable |
@wraps.dev/sms)Base class every other SMS SDK error extends. Use it for a single catch-all instanceof check that covers all of them.
Thrown when an AWS End User Messaging API call fails.
| Property | Type | Description |
|---|---|---|
code | string | AWS error code |
retryable | boolean | Whether the request can be retried |
Thrown when input parameters are invalid.
| Property | Type | Description |
|---|---|---|
field | string | Which field failed validation |
message | string | Human-readable error description |
Thrown when the recipient has opted out of receiving messages.
| Property | Type | Description |
|---|---|---|
phoneNumber | string | The phone number that opted out |
Thrown when the sending rate limit has been exceeded.
| Property | Type | Description |
|---|---|---|
retryAfter | number | Seconds to wait before retrying |
No Automatic Retries
The SDKs do NOT automatically retry failed requests. If retryable is true, implement your own retry logic using the pattern below.
Use exponential backoff when retrying failed requests. This example uses a simple retry loop with increasing delays.
import { WrapsEmail, SESError } from '@wraps.dev/email';const email = new WrapsEmail();async function sendWithRetry(params, maxRetries = 3) { for (let attempt = 0; attempt < maxRetries; attempt++) { try { return await email.send(params); } catch (error) { if (error instanceof SESError && error.retryable && attempt < maxRetries - 1) { await new Promise(r => setTimeout(r, Math.pow(2, attempt) * 1000)); continue; } throw error; } }}Catch and handle specific error types to provide appropriate responses in your application.
import { WrapsEmail, SESError, DynamoDBError, ValidationError } from '@wraps.dev/email';const email = new WrapsEmail();try { await email.send({ from: 'hello@yourdomain.com', to: 'user@example.com', subject: 'Hello', html: '<p>Hello!</p>', });} catch (error) { if (error instanceof ValidationError) { console.error('Invalid input:', error.field, error.message); } else if (error instanceof SESError) { console.error('SES error:', error.code, error.retryable); } else if (error instanceof DynamoDBError) { console.error('DynamoDB error:', error.code, error.retryable); }}Full API reference for the @wraps.dev/email TypeScript SDK.
View ReferenceFull API reference for the @wraps.dev/sms TypeScript SDK.
View ReferenceConfigure AWS credentials and permissions for Wraps.
View Guide