name: sst-ops description: >- sst (Ion) deploy and state operations: bridging aws-login credentials, diagnosing stale locks, targeted-deploy/refresh hazards, and repairing a corrupted Pulumi snapshot. Use when running sst deploy/diff/refresh/unlock, when sst reports missing AWS credentials, a concurrent-update lock, or "Your app state is corrupted".
sst-ops
Operational gotchas for sst (Ion, v4+), learned the expensive way. sst embeds
Pulumi with a DIY backend: state lives in the bootstrap S3 bucket at
s3://<bootstrap-state-bucket>/app/<app>/<stage>.json, is pulled locally per
command, and pushed back after.
Credentials: aws login sessions
The AWS CLI's newer aws login flow (~/.aws/config has login_session, no
credentials file) works for the CLI but is invisible to sst/Pulumi's Go SDK —
sst falls through to EC2 IMDS and reports "AWS credentials are not
configured" even though aws sts get-caller-identity succeeds. Bridge it
per-command:
eval "$(aws configure export-credentials --format env)" && npx sst deploy ...
Also check for an empty-string AWS_PROFILE in the environment; the CLI
treats it as unset but Go SDK credential chains can choke on it.
Locks
Locked: A concurrent update was detected often means an earlier deploy died
mid-flight, not that one is running. Before sst unlock, check ps for live
sst/pulumi processes (and consider other machines/CI). A dead deploy may
have half-applied cloud changes — e.g. a DynamoDB GSI still in CREATING —
so after unlocking, expect the next deploy to hit ResourceInUseException
until AWS settles. Poll the resource to ACTIVE first rather than retrying
blind.
Targeted operations
sst deploy --target <Resource>works and is the right tool for converging one drifted resource without touching the rest of the stack (it passes--target-dependentsto Pulumi).- Do not run
sst refresh --target <Resource>. Observed on sst 4.17 / Pulumi 3.215: the targeted refresh rewrote the snapshot with duplicate live copies of dozens of resources ("duplicate resource … not marked for deletion"), after which every command failed with "Your app state is corrupted" andsst state repairno-oped. Refresh the whole stage or not at all.
Snapshot repair (when sst state repair no-ops)
Duplicate URNs whose extra copy has delete: true are legal pending-deletes;
the integrity failure is URNs with two live (delete absent/false)
copies.
- Back up, then pull the state:
aws s3 cp s3://<state-bucket>/app/<app>/<stage>.json ./state.json(copy a timestamped backup object in S3 first). - Walk
checkpoint.latest.resourcesin order: keep everydelete:trueentry; keep the first live copy of each URN; drop later live duplicates. Order stays valid because the first block is the refreshed canonical ordering (parents before dependents). - Verify zero live duplicates remain, upload back, re-run the command. A successful read (even if the command later fails for unrelated local reasons) confirms integrity is restored.
Misc
- Treat
sst diffoutput as sensitive. Components backed by Pulumicommand:local:Command(including the Next.js builder) can include the current process environment in the raw property diff, which may expose temporary cloud credentials or application secrets. Inspect it only in a private terminal and filter/redact before sharing or recording it. sst diff/deployevaluates the whole program even with--target, so a Next.js site component fails on missing.open-next/**/resource.encwhen the site was never built locally. That is a missing local artifact, not state corruption; a full deploy rebuilds it.- After repairing state or any manual S3 state surgery, run the cheapest
command that reads state (
sst diff --target <Resource>) to validate before attempting a real deploy.
