wispwisp
Reference

Error codes

Every diagnostic wisp can emit, what triggers it, and how to resolve it.

Every wisp diagnostic carries a stable code. They're grouped by family, and the family tells you which stage of the pipeline rejected your build.

FamilyMeaningRaised by
WISP-CONFIGA primitive was declared in a way wisp can't read staticallyAnalyse
WISP-SCOPEA primitive was constructed somewhere it can't be foundAnalyse
WISP-DUPTwo resources share a nameValidate
WISP-IAMA generated policy isn't narrow enough to shipEmit
WISP-SHARDInformation about splitting into nested stacksEmit
WISP-AONA resource would cost money while idleGate

WISP-CONFIG

These mean wisp read your declaration and couldn't turn it into a resource. Almost always the fix is to inline a literal value instead of computing one, because wisp never runs your code while compiling.

WISP-CONFIG-001

The most common one. A primitive's name, path, options object, or handler wasn't something wisp could read statically.

// Rejected — options are computed at runtime.
export const orders = new Store("orders", buildOptions());

// Accepted.
export const orders = new Store("orders", {
  partitionKey: { name: "id", type: "S" },
});

The same rule covers route paths ("/orders/{id}" must be a literal), handlers (must be written inline, not passed by reference), and .override() / addRawResource() arguments.

WISP-CONFIG-002

A queue already has a consumer. A Queue can have at most one — split the work inside your handler, or use a second queue.

WISP-CONFIG-003

.override() was chained onto a route, consumer, schedule, or bucket handler. Per-handler overrides aren't supported; call .override() on the standalone Api, Store, Queue, Schedule, or Bucket instead. See escape hatches.

WISP-CONFIG-004

An addRawResource() logical id collides with one wisp generates. Pick a different id.

WISP-CONFIG-005

Two ctx.step() or ctx.sleep() calls in the same Workflow share a name. Step names are how a workflow finds its place after a restart, so they have to be unique.

WISP-CONFIG-006

A ctx.step() or ctx.sleep() call is nested inside an if, loop, try, or switch. Workflow steps must be a flat, sequential list at the top level of the handler. Ordinary code can go anywhere between them.

WISP-CONFIG-007

A workflow used ctx.parallel(), ctx.map(), or waitForCallback(). These aren't implemented yet — today's Workflow supports ctx.step and ctx.sleep.


WISP-SCOPE

WISP-SCOPE-001

A primitive was constructed somewhere other than the top level of a module — inside a function, a conditional, or a loop. wisp has to be able to enumerate every resource without executing anything, so construction has to happen where it can be seen:

// Rejected.
function setup() {
  return new Store("orders", { partitionKey: { name: "id", type: "S" } });
}

// Accepted.
export const orders = new Store("orders", {
  partitionKey: { name: "id", type: "S" },
});

WISP-DUP

WISP-DUP-001

Two resources of the same kind share a name. The message includes where the first one was declared. Names become part of the physical AWS resource name, so they have to be unique within a stage.


WISP-IAM

These are internal safety checks on the policies wisp itself generates. Seeing one usually means an escape hatch introduced something too broad.

WISP-IAM-001

A policy statement uses Resource: "*". Every statement must name a specific ARN.

WISP-IAM-002

A policy statement uses a wildcard action. Every action must be named explicitly.

WISP-IAM-003

A DynamoDB table isn't on PAY_PER_REQUEST billing. Provisioned capacity bills whether or not anyone reads or writes, which breaks the $0 idle guarantee.


WISP-SHARD

WISP-SHARD-001

A warning, not an error. Your app was large enough to be split into nested stacks. Nothing is required of you — see stack sharding for what changes.


WISP-AON

The always-on family. Each code identifies a resource type that costs money with zero traffic, and each one blocks the deploy. The full table — every code, its monthly idle cost, and the $0 alternative — lives in the always-on denylist.

CodeDetects
WISP-AON-001RDS instance
WISP-AON-002RDS cluster
WISP-AON-003NAT gateway
WISP-AON-004Application/network load balancer
WISP-AON-005ElastiCache cluster
WISP-AON-006ECS service
WISP-AON-007OpenSearch domain
WISP-AON-008MSK (Kafka) cluster
WISP-AON-009Lambda provisioned concurrency
WISP-AON-010Lambda attached to a VPC (requires a NAT gateway for egress)
WISP-AON-011EC2 instance
WISP-AON-012DynamoDB table with provisioned throughput

Unlike every other family, these can be deliberately allowed:

export default {
  app: "my-app",
  region: "eu-north-1",
  allowAlwaysOn: ["WISP-AON-003"],
};

Doing so is loud rather than silent: the violation is still printed on every build, and wisp estimate reports the guarantee as broken.

On this page