Skip to content

spawn list --state all silently returns nothing — "all" is passed through as a literal instance-state-name filter #527

Description

@scttfrdmn

Severity: high — the documented way to check for leaked instances cannot fail

spawn list --state all returns an empty list always, regardless of how many instances
exist. The string all is passed through as a literal EC2 instance-state-name filter value,
and no instance is ever in state "all", so the filter matches nothing. Exit code is 0 and the
output is a well-formed empty array, so it reads exactly like a clean account.

pkg/aws/client.go:996-1008:

// Add state filter if specified
if stateFilter != "" {
    filters = append(filters, types.Filter{
        Name:   aws.String("instance-state-name"),
        Values: []string{stateFilter},          // <-- "all" goes straight through
    })
} else {
    // Default: show running and stopped instances (not terminated)
    filters = append(filters, types.Filter{
        Name:   aws.String("instance-state-name"),
        Values: []string{"pending", "running", "stopping", "stopped"},
    })
}

There is no "all" case anywhere on the path — grep -n '"all"' cmd/list.go pkg/aws/client.go
returns nothing. cmd/list.go:45 binds --state as a free-form string
("Filter by instance state (running, stopped, etc.)") and cmd/list.go:81 hands it to
ListInstances unmodified.

So the flag whose name promises a superset of the default actually produces the empty set —
strictly less than passing no flag at all.

This is documented, which is what makes it costly

docs/tools/spawn.md:95 in the spore-host docs repo:

### `spawn list`

List your running (or all) instances:

    spawn list
    spawn list --state all        # <-- documented; returns nothing
    spawn list --region us-east-1

A user following the docs to answer "did anything leak?" gets [] and stops looking. That is
the same failure shape as #47 (e2e tests leaking 67 instances, including one running 12 days):
the leak was invisible until someone checked a different way.

Reproduction

spawn launch listcheck --region us-west-2 --ttl 1h -y
spawn list -o json                      # -> the instance
spawn list --state all -o json          # -> []          ← wrong
spawn list --state running -o json      # -> the instance

Expected behaviour

--state all should mean no state filter at all — every state including terminated and
shutting-down, i.e. a genuine superset of the default. Concretely: special-case
all (and probably any) in listInstancesInRegion to append no
instance-state-name filter.

The second half matters as much: an unrecognised --state value must be an error, not an
empty result.
--state runing, --state ALL, --state active all currently return []
with exit 0. Validate against the EC2 enum (pending, running, shutting-down,
terminated, stopping, stopped) plus the all alias, and reject anything else naming the
valid values. Without that, this bug just moves to the next plausible-sounding word.

Note that fixing all to include terminated will change what the command prints in normal
use — a long-lived account accumulates terminated instances that AWS retains for about an
hour. That is the correct behaviour for a flag called all, and it is why the default (which
deliberately excludes terminated) should stay exactly as it is.

Regression test to add

Tier 0, against Substrate:

given  one running instance and one terminated instance
when   spawn list --state all -o json
then   BOTH are returned

when   spawn list -o json            (no flag)
then   only the running one is returned

when   spawn list --state bogus -o json
then   exit != 0 with an error naming the valid states  (NOT exit 0 and [])

The third case is the important one. This bug is a check that cannot fail, so a test that only
asserts --state all returns something would still pass if the flag silently degraded to
some other wrong-but-non-empty filter. Assert the exact membership, and assert that garbage is
rejected.

Cross-references

Found while writing the #524 regression test for the cost-to-result project. That project had
also recorded spawn list --state all -o json --region us-west-2[] as evidence that its
AWS account was clean before any spend; that evidence was worthless and has been retracted and
re-taken with bare spawn list.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:lifecycleTTL / idle / on-complete / hibernation / reaperbugSomething isn't workingspend-safetyBugs where a cost control does not do what it says — real-money risk

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions