Saphan StudioDocs
Troubleshooting

Machines and seats

Refusals met while admitting a machine, seating work on it, changing its settings and retiring it.

42 classes. 4 explained, 38 named only.

This is the largest family and the least documented one, and it is worth saying why: admitting a machine, seating an identity on it, changing its capacity and retiring it are all owner acts written into a signed log, and each one has its own guard. The guards are numerous because the acts are irreversible in the record.

Every refusal in this family writes nothing. A refused admission does not half-admit; a refused seating leaves the slot as it was.

The refusals here print their own remedy verbatim. Where a class below is explained, the product prints the same fix at the terminal, with your values filled in. Read the printed line before you copy anything from this page.

Two shapes of refusal you will meet before any class

Some things that stop an admission are not classes at all, but plainly worded requirements. If the product tells you that exactly one credential form is required, or that certain identity fields are all required, that is a complete instruction — supply what it names and re-run.

The certificate is not for this machine

Class: machine-cert-subject-mismatch.

What it means. The certificate subject does not match the machine identifier you passed.

What to do. Admit with the credential issued for this exact machine identifier. ⛔ Do not change the identifier to match the certificate unless that is genuinely the machine you meant — the identifier is what everything else in the record refers to.

The transport is SSH and no host was given

Class: machine-ssh-host-required.

What it means. You chose the SSH transport and did not say which host.

What to do. Pass the host. Two defaults are worth knowing while you are here: the SSH key autodetects the usual key file names in your SSH directory, and the port defaults to 22 — so a non-standard port must be given explicitly.

There is one deliberate escape hatch, and it is honest about itself. Admission can be forced past a failing live SSH identity probe, and when it is, the row is annotated as unverified. The record carries the fact rather than hiding it, so a later reader can tell a probed machine from an unprobed one.

A device key is already at the target path

Class: machine-device-key-exists.

What it means. The one-command admission for the machine you are typing on mints a device key, and one already exists where it would write.

What to do. The refusal prints the remedy verbatim, with the path filled in. It offers exactly two paths, and the choice is yours to make:

  1. Reuse the existing key rather than minting a new one, which is what you want in nearly every case.
  2. Point the mint at a different path if you genuinely want a fresh key.

⚠ Note that this one-command form is local-only: it refuses an SSH transport, and it refuses to be combined with a supplied certificate or signing request.

The billing class is not one of the three

Class: machine-seat-billing-class-unknown.

What it means. A seat's billing class is a closed vocabulary of exactly three words: subscription, metered, local-energy. Anything else refuses by name and writes nothing.

What to do. Use one of the three — or omit the flag entirely, which is legal and is the right answer for "I have not decided yet". A seat written with no billing class reads as unknown everywhere it is projected.

Unknown is deliberately not the same as subscription, and nothing in this system treats it as such. Billing class is an owner's act, never a guess.

Changing a seat's class later is the same act again — a fresh seating for that identity naming the new class, appended to the signed log like any other seating. Nothing is overwritten.

Named, not yet explained

These 38 classes are real, deliberate and named. This section does not yet carry their resolution. The names are grouped here the way the acts are, so a name you are holding lands next to its neighbours.

Admitting and retiring a machine

classwhat its name indicates
machine-already-admittedThe machine is already in the registry.
machine-already-retiredThe machine has already been retired.
machine-not-admittedThe machine named is not in the registry at all. ⚠ Check the identifier against what the registry lists — before any machine exists, the registry says so plainly rather than printing an empty table.
machine-cert-expiredThe credential offered has passed its validity period.
machine-invalid-csrThe certificate signing request offered is not well formed.
machine-retire-live-runRetirement was refused because work is running on the machine.
machine-retire-seatedRetirement was refused because an identity is still seated on the machine.
machine-renew-no-changeA renewal was asked for that would change nothing.

Transport, keys and host pinning

classwhat its name indicates
machine-transport-requiredNo transport was named, and none is guessed for you.
machine-ssh-key-requiredThe SSH transport needs a key and none was given or autodetected.
machine-ssh-key-unverifiedThe SSH identity did not pass its live probe. ⚠ Related: admission can be forced past this, and the row is then annotated as unverified.
machine-pin-not-sshA host pin was offered for a machine that is not on the SSH transport.
machine-pin-ssh-host-missingA host pin was offered with no SSH host to pin.
machine-pin-operator-entry-missingA host pin has no corresponding operator entry.
machine-pin-revokedThe host pin in question has been revoked. ⚠ Worth knowing on sight: host key rotation is expected on machines that regenerate keys at boot and unexpected elsewhere, and rotation is always recorded.

Seating an identity

classwhat its name indicates
machine-seat-slot-occupiedAnother identity already holds that slot.
machine-seat-slot-out-of-rangeThe slot number is outside the machine's slot count.
machine-seat-role-unknownThe role named is not one the product knows.
machine-seat-backend-unknownThe backend named is not one the product knows.
machine-seat-backend-not-seatableThe backend named exists and cannot hold a seat.
machine-seat-lane-requiredThis backend needs a lane named and none was given.
machine-seat-lane-on-config-dir-backendA lane was given for a backend that takes a configuration directory instead.
machine-seat-config-dir-on-lane-backendA configuration directory was given for a backend that takes a lane instead.
machine-seat-cast-path-invalidA path supplied with the seating is not usable.
machine-seat-cross-machine-moveA seat cannot be moved from one machine to another in a single act.
machine-unseat-not-seated-hereThe identity is not seated on the machine you named.
machine-unseat-live-runUnseating was refused because work is running.

Capacity, pricing and paths

classwhat its name indicates
machine-slots-invalidThe slot count offered is not a valid count.
machine-slots-no-changeThe slot count offered is the one already recorded.
machine-slots-below-seatedThe new slot count is lower than the number of identities already seated.
machine-max-sessions-invalidThe session ceiling offered is not a valid value.
machine-max-sessions-no-changeThe session ceiling offered is the one already recorded.
machine-max-sessions-slot-out-of-rangeThe slot named for a session ceiling is outside the machine's slots.
machine-price-invalidThe price offered is not a valid value.
machine-price-no-changeThe price offered is the one already recorded.
machine-set-binary-invalid-pairTwo settings for the machine's program were given that cannot both apply.
machine-worktrees-root-invalidThe directory named as the root for work areas is not usable.
machine-gomodcache-root-invalidThe directory named as the root for the language toolchain's module cache is not usable.

A "no change" refusal is not a failure of yours. Several acts above refuse rather than record an act that would alter nothing, because the log is append-only and a no-op entry in it is a lie about what happened.