cdkd force-unlock
Deletes the lock object a cdkd run holds while it writes to a stack. Reach for it when a run was force-quit, a CI job was cancelled, or a laptop slept mid-deploy, and the next command refuses to start because the stack is still locked.
It needs no CDK app — it operates on the state bucket directly.
cdkd force-unlock MyStack # every region where the stack has state
cdkd force-unlock MyStack --stack-region us-west-2 # one region
cdkd force-unlock MyStack OtherStack # several stacks in one run
Options
| Flag | Default | Description |
|---|---|---|
--stack-region <region> |
every region where the stack has state | Release the lock for one region only. |
--stack <name> |
— | Stack to unlock, as a flag instead of a positional argument. |
--state-bucket <bucket> |
CDKD_STATE_BUCKET, then cdk.json, then the account default |
The state bucket holding the lock. The default name is resolved by probing cdkd-state-{accountId} and the legacy cdkd-state-{accountId}-{region}. |
--state-prefix <prefix> |
cdkd |
Key prefix inside the bucket. |
--profile <profile> |
— | AWS profile. |
--role-arn <arn> |
CDKD_ROLE_ARN |
Role to assume before any AWS call. |
-y, --yes |
false |
Accepted for consistency with the other state-writing commands; cdkd force-unlock asks no confirmation, so it changes nothing. |
--verbose |
false |
Debug-level logging. |
At least one stack is required, positionally or via --stack; the command
errors rather than guessing.
Usually you do not need this
A lock usually clears itself. It carries a 30-minute TTL, and a live cdkd
process renews it well inside that window, so an abandoned lock is expired 30
minutes after its owner's last renewal — and the next cdkd deploy normally
takes an expired lock over on its own, printing a warning that names the
previous owner and how long ago it expired.
cdkd force-unlock is for the cases where that does not happen:
- You do not want to wait out the remaining TTL.
- The lock object is unreadable — truncated, or holding something that is not a
lock. Nothing can take that over automatically, because the takeover path has
to read
expiresAtto decide the lock is expired, while every attempt to acquire still fails on the object's presence.force-unlockdeletes it regardless of whether it could be parsed. - The takeover was refused. cdkd removes an expired lock only when it can delete the exact version it just read; if that version cannot be identified, or the S3 endpoint will not evaluate the conditional delete, it declines rather than risk deleting a lock some other process has since taken. It says so and names this command. Such a lock does not clear on its own, however long you wait.
The delete is unconditional
This command removes the lock whether or not it has expired, and whether or not another process still holds it. That is deliberate — a lock cdkd cannot read is a lock nothing else can clear, so gating the delete on reading it first would make a corrupt lock permanent.
The cost is that running it against a live deploy leaves two processes writing
to the same stack. Before running it, confirm the owner is really gone: the
command prints the lock's owner and operation before deleting, and cdkd events <stack> shows whether a run is still emitting events.
Confirm the account, too. The command re-resolves the state bucket from the
ambient profile, so a shortened invocation — dropping the --profile or
--state-bucket you used earlier — can clear the lock of a same-named stack in
a different account. When cdkd prints a recovery command in a lock error, run it
as printed.
Which regions it releases
Without --stack-region, the command looks the stack up in the state bucket and
releases the lock in every region where that stack name has a state record —
the same stack name deployed to two regions has two independent locks. A record
written by a pre-v2 cdkd that names no region resolves to the region-less lock
key, and is released by the same walk; one that does name a region resolves to
the region-prefixed key only.
When the stack has no state record at all, the walk falls back to the region
--region or AWS_REGION names, and to the literal us-east-1 when neither is
set — the profile's own region is not consulted. See
--region / AWS_REGION.
--stack-region narrows the walk to the one region named, and no other key is
touched — including the region-less one.
What it deletes
The lock is a single object at
s3://{bucket}/{prefix}/{stackName}/{region}/lock.json. The command deletes it
and then purges that key's noncurrent versions, so a versioned state bucket does
not accumulate the leavings of every crashed run. Nothing else in the state
record is touched: the stack's state.json, its resources, and its deployment
events are all left as they were.
Exit codes
| Code | Meaning |
|---|---|
0 |
Every lock the walk reached was deleted, or there was none to delete. |
1 |
A lock could not be deleted; no stack was named; credentials or state-bucket resolution failed; or listing a stack's regions failed. |
A lock that was already absent is a success, not a failure — the command's job is that no lock remains, and an absent one already satisfies it.
The two failure kinds differ in how much of the walk still runs:
- Failing to DELETE a lock does not stop the walk. The error is reported,
the remaining regions of that stack and then the remaining stacks are still
attempted, and the run exits
1at the end naming every lock it could not release. One unreachable stack therefore does not cost you the others. - Failing to LIST a stack's regions aborts immediately with
1, leaving any later stack in the same invocation unattempted.
The full cross-command table is in the CLI Reference.
Related
cdkd state— inspecting the state record the lock guardscdkd events— checking whether a run is still active before forcing the lock- State Management — the state bucket layout and the locking model
- CLI Reference — every command and the full exit-code table