cdkd state: malformed records
A state record is a JSON file in S3. A hand edit or a truncated write can
leave it in a shape cdkd does not expect. The cdkd state
subcommands then follow one rule:
- The read-only subcommands (
list,resources,show) show what they can and say on stderr what they could not show. - The subcommands that would have to rely on the record's contents to write refuse it and change nothing.
This page lists what you see in each case. To look at the stored values exactly as they are, run:
cdkd state show MyStack --json
Rows state list --long could not read
cdkd state list --long reads every stack's record and lock. When one of
those reads fails, the listing continues. The affected row says what is
missing, a warning on stderr counts such rows, and the command still exits
0.
Two reads can fail, and each failure leaves the other value in place:
- The record. The row shows
Resources: unknown (...), and the lock is still reported. With--long --json,resourceCountisnull, andstateReadErrorandstateReadErrorKindare set. - The lock. The row shows
Lock: unknown (...), and the resource count is still reported. With--long --json,lockedisnullandlockReadErroris set.
The text in parentheses gives the reason. Run cdkd state show on that stack
to see the underlying error.
stateReadErrorKind
A script can use stateReadErrorKind to decide whether a retry is worthwhile:
stateReadErrorKind |
Meaning | Retry? |
|---|---|---|
read-failed |
The record could not be read. | Possibly; the failure may be transient. |
resources-malformed |
The record was read, but its resources is not a JSON object. |
No. Fix the record. |
Treat a value you do not recognise as a failure. More kinds may be added.
Names that are quoted or cut
cdkd prints a stack name or region as it is when the value is a plain identifier. Every real stack name and region is one, so you normally never see anything else.
A hand-edited record or S3 key can hold other characters. A value with a
space, a bracket, a quote or a non-ASCII character is printed inside double
quotes. Without the quotes, such a value could be read as a different stack's
row. A very long value is cut and ends with
[cut: N more characters withheld, tail sha256:<32 hex>].
The quotes mark where the value starts and ends for a person reading the output. They are not shell quoting, so do not paste a quoted value into a command.
Use --json when a script needs the exact stored values. --json output is
never quoted or cut this way. Control characters in it are written as \uXXXX
escapes, so the values parse back unchanged. That holds for the --json mode
of every cdkd state subcommand.
What the human views do to a malformed record
The text output of cdkd state resources and cdkd state show treats every
value in the record as untrusted text. A stored value therefore cannot change
what the output appears to say:
- Control characters are removed from names, types, ids and dependency lists. A value cannot forge an extra output line or run a terminal escape sequence.
- A value of the wrong type is still printed. A number prints as that number
and an object prints as JSON. A value that cannot be serialized prints as
(unserializable). - A lock whose expiry cannot be read prints
expires at an unknown time.
Records that are refused outright
state resources and state show refuse a record that is broken at its root.
The message names the problem. This happens when:
state.jsonis not valid JSON,- its body is not a JSON object, or
- its schema version is one this cdkd does not read.
cdkd state list --long does not refuse. It marks only that stack's row, as
described under
Rows state list --long could not read.
--json and the stored values
cdkd state show --json prints the stored values without any of this
filtering. cdkd state resources --json is different: it prints the resource
list cdkd derived from the record, so a malformed part of the record does not
appear in it.
When resources is not an object
In a healthy record, resources is a JSON object that maps each logical id to
a resource. A damaged record can hold a string, a list, a number or a boolean
there instead. The read-only views then treat the stack as having no resources
and print a warning on stderr.
| Command | What it prints |
|---|---|
cdkd state resources, plain and --long |
Nothing, plus the warning. |
cdkd state resources --json |
[], plus the warning. |
cdkd state show |
Resources (0): and no resource blocks, plus the warning. |
cdkd state show --json |
The stored value, unchanged. No warning. |
cdkd state list --long |
Resources: unknown (...) for that row. |
An absent or null resources is reported the same way, with one difference:
cdkd state list --long counts it as 0 and gives no reason.
Warning
cdkd deployandcdkd destroyrefuse such a record. If they read it as empty, a deploy would create every resource again, and a destroy would delete none of them and then remove the record. Repair the record or remove it. State Management lists what each command does.
With --show-nested
cdkd state show --show-nested judges each record in the tree separately. A
healthy parent with a malformed nested stack prints the parent's resources and
reports the child with none. It prints one warning for each record it read
that way.
--show-nested --json prints the stored values unchanged, like plain
--json, but it also prints one warning per malformed record.
When a value container is not an object
Four other parts of a record are JSON objects too: outputs and
skippedOutputs on the record, and attributes and properties on each
resource. The same rule applies to them. One that holds a string, a list, a
number or a boolean is read as empty. cdkd prints one warning per record, and
the warning names each part it read as empty.
| Command | What it prints |
|---|---|
cdkd state show |
The record without that block, plus the warning. |
cdkd state resources --long |
Attributes: (none), plus the warning. |
cdkd state resources --json |
"attributes": {}, plus the warning. |
cdkd state resources, plain |
The usual three columns. No warning. |
cdkd state show --json |
The stored value, unchanged. No warning. |
In cdkd state show, "without that block" means the following. An emptied
outputs prints no Outputs: block, and an emptied skippedOutputs prints
no Skipped outputs: block. An emptied attributes prints
Attributes: (none), and an emptied properties prints Properties: (none).
Three details:
- Plain
cdkd state resourcesprints no warning because it prints no attributes. - With
--show-nested, the warning names the stack whose record it was.--show-nested --jsonprints the stored value with no warning. - An absent or
nullcontainer is normal and is not reported.
What the writing subcommands refuse
The subcommands that write do not guess at a malformed record:
cdkd state refresh-observedrefuses before it takes the lock or reads anything from AWS.cdkd state orphan --resourcerefuses and writes nothing.
cdkd state orphan without --resource deletes the whole record, so it is
the way to remove a record you cannot repair. The AWS resources stay.
When a nested resource shows the wrong provisionedBy
This section is about a record that is well formed but holds a stale value.
cdkd state show prints a ProvisionedBy line for each resource. It names
the layer that created the resource: sdk or cc-api (the AWS Cloud Control
API). A resource in a nested stack can carry cc-api from an older cdkd
release. In that release, --recreate-via-cc-api <LogicalId> also matched a
nested stack's resource that had the same logical id.
The value stays in the record, and you cannot edit it:
- The recreate flags cannot target a resource in a nested stack.
- No
cdkd statesubcommand changes the label. The two layers store different physical ids for many types, so changing the label alone would hand the SDK provider an id it cannot use.
Check whether the type moves back by itself
Some types return to the default layer without any action. Deploy: safety and State Management list them.
Otherwise, rename the construct
Rename the construct in the nested stack, which changes its logical id:
// Before: new sqs.Queue(this, 'Jobs');
new sqs.Queue(this, 'JobsV2');
The next deploy creates the new logical id through the default layer. It then deletes the old resource through the layer that created it.
Caution
This destroys and re-creates the resource, and the deploy does not ask first. A stateful resource comes back empty. With a
Snapshotdeletion policy, the RDS default, the old one is deleted after a final snapshot.
Prepare these cases before you rename
The deploy creates the new resource before it deletes the old one. Four kinds of old resource need preparation:
- A non-empty S3 bucket without
autoDeleteObjects. Empty it first. Otherwise its delete fails, and the rollback deletes the new bucket again. With--no-rollback, both buckets are left. - A resource with deletion protection. Deploy the property set to
falseon its own, before the rename. A deploy never lifts protection, and the renamed resource would be created protected, so the rollback could not delete it either. - A resource with a fixed physical name. Give the new one a different name. Or deploy the removal first and add the construct back in a second deploy.
- A resource with
DeletionPolicy: Retain. No preparation is needed, but the old resource stays in AWS afterwards and is only dropped from state. Delete it yourself. If it has a fixed physical name, delete it before the deploy that adds the construct back.
Related
cdkd state: the read-only subcommands this page describes- cdkd state: writing subcommands:
orphan,destroy,migrateandrefresh-observed - State Management: the record schema, and what
cdkd deployandcdkd destroydo with a malformed record - cdkd state internals: the full rendering rules, for contributors