Skip to content

Errors and what they mean

Every failure below is quoted as the command actually prints it, with what caused it and what to do. Messages are shown without the Error: prefix that Cobra adds in front of them.

Nothing here calls os.Exit — the commands return a Go error and the host binary decides the exit code. See Configuration and environment.

Sentinel errors you can match on programmatically

Four are exported from this module, for callers driving the commands in-process:

Value Raised when
signingcli.ErrKeyFileExists A keys output file exists and --force was not passed.
signingcli.ErrKeyAlreadyPublished keys publish found different bytes already published at the same path or manifest entry.
signingcli.ErrInvalidProject keys publish --project is not a safe path segment.
signingcli.ErrInvalidStatus keys publish --status is not active, retired or revoked.

Errors are wrapped with github.com/cockroachdb/errors, so errors.Is works through the wrapping. Failures that originate upstream carry the upstream sentinels instead — signing.ErrUnknownBackend, openpgpkey.ErrUnsupportedKeyType, and the local backend's ErrMissingPEMBlock, ErrUnsupportedKeyType and ErrEncryptedPEMUnsupported.

The backend could not be found or used

"nope" (available: local): unknown signing backend

--backend names something this binary did not compile in. The parenthesised list is everything that is available. Fix the flag value, or blank-import the backend you wanted — Compile in signing backends.

"aws-kms" (no backends are registered — this binary was built without any signing backends compiled in): unknown signing backend

The host binary imported signing-cli but no backend package at all. sign and keys mint exist and can never succeed. Add a blank import to main.

constructing signer: ./key.priv.asc: no PEM block found in file

The local backend was handed a file that is not PEM. The usual cause is pointing --key-id at an armored OpenPGP private half (.priv.asc, the default from keys generate --algorithm ed25519) rather than a PEM. Regenerate with --private-format pem, or point at the .pem file you already have.

constructing signer: encrypted PEM private keys are not supported in v0.1; decrypt out-of-band first or use the aws-kms backend

The PEM's header is ENCRYPTED PRIVATE KEY. The local backend has no decryption path. Decrypt the file yourself first, or move the key into a KMS.

The key is the wrong type for the format

got ed25519.PublicKey: unsupported key type: only RSA is supported

Raised by keys mint (prefixed minting armored public key:) and by sign --format openpgp (prefixed computing signature: DetachSign:). The OpenPGP path handles RSA keys only. Use an RSA key for OpenPGP, and the Ed25519 key for --format minisign. See OpenPGP signing is RSA-only.

minisign requires an Ed25519 signing key, but --key-id resolved to *rsa.PublicKey; RSA keys sign the OpenPGP manifest path (--format openpgp) instead

The mirror case, from sign --format minisign and keys minisign. The check runs before any signing happens, so nothing was written.

computing signature: signer's RSA public half does not match the public key block — wrong key?

sign --format openpgp resolved a signer whose public half is not the one in --public-key. Both are RSA, but they are different keys. The signature would name a fingerprint it was not made with and fail for every verifier, so sign refuses. Check that --public-key is the .asc matching this --key-id.

The timestamp was rejected

computing signature: computing detached signature: openpgp: invalid argument: no valid signing keys

Almost always --created set to an instant before the signing key's creation time. That value is used as "now" while the signature is built, so the key looks not-yet-valid. Compare the two — gpg --list-packets release.asc prints the key's created field as Unix seconds — and move --created forward.

parsing --created "not-a-time" as RFC3339: …

--created must be RFC3339, e.g. 2026-01-01T00:00:00Z.

parsing SOURCE_DATE_EPOCH "nope" as Unix seconds: …

SOURCE_DATE_EPOCH must be an integer count of seconds. A malformed value fails the run rather than being ignored. It is read only by sign --format minisign.

The flags contradict each other

unknown --format "zzz"; valid values are "openpgp" and "minisign"

Only those two.

--public-key is required for --format openpgp

An OpenPGP detached signature takes its identity from the public-key file. This is checked before the backend is contacted, so it fires even when --backend is also wrong.

--public-key is not accepted for --format minisign: a minisign signature carries its own key identifier, so the identity comes from --backend / --key-id

Drop the flag. It is refused rather than ignored, because passing it means you believe an OpenPGP identity is in play when it is not.

--project is only meaningful for --format minisign; an OpenPGP signature has no trusted comment to record it in

There is nowhere in an OpenPGP signature to put it.

--append is not supported for --format minisign; it is an OpenPGP dual-sign primitive

--append merges OpenPGP signature packets into one armored block. A minisign signature file holds exactly one signature.

--private-format openpgp is not supported for --algorithm rsa; RSA private halves are written as PKCS#1 PEM

RSA has only one on-disk private form here. Drop --private-format.

unknown --private-format "der" (expected "openpgp" or "pem")

Only those two.

--rsa-bits must be 2048, 3072, or 4096 (got 1024)

Those three sizes, and only for --algorithm rsa; the flag is ignored for Ed25519.

unknown algorithm "dsa" (expected "ed25519" or "rsa")

keys generate --algorithm takes those two.

--output (z.pem) must differ from --private-output (z.pem)

keys generate writes two files and will not point them at one path.

required flag(s) "backend", "key-id" not set

Cobra's own message for a missing required flag. sign and keys mint require --backend and --key-id; keys mint also requires --name and --email; keys generate requires --algorithm, --name and --email; keys wkd requires --domain (even with --method direct, which does not use it in the path); keys publish requires --project.

The output path was refused

refusing to write signature to "art.txt" which equals the input path "art.txt"

sign will not overwrite the file it is signing. The check compares cleaned paths and, when both already exist, compares inodes — so a symlink or a different spelling of the same file is caught too.

writing output: "release.asc" (pass --force to overwrite): output file already exists

A keys subcommand found its output already there. Pass --force, or choose a different path. sign does not behave this way — it overwrites silently. The prefix names which write failed: writing output: from keys mint, writing private-half output: or writing public-half output: from keys generate, and writing <path>: from keys minisign --output.

--output "-" (stdout) is not supported; pass a file path

keys mint writes to a file only, so the armored bytes cannot interleave with log lines. keys minisign is the command that writes to stdout.

writing ./keys-site: open keys-site: is a directory

--output pointed at an existing directory.

The public key file could not be read

parsing g.txt as armored OpenPGP: openpgp: invalid argument: no armored data found

keys wkd was given a file that is not an armored OpenPGP key.

key release.asc has no UID with a parseable email

keys wkd groups keys by the email in their User IDs. A key without one cannot be placed in a bucket.

parsing rsa.asc: body is not valid base64: malformed minisign public key

keys publish takes a minisign public-key file — the two-line form from keys minisign --output — not an armored OpenPGP .asc.

The WKD tree could not be written

no input key matched --email nobody@example.com

Every --email must match at least one input key's UID, otherwise you would publish an empty bucket. Check the address, or drop --email to publish every address found across the inputs.

writing WKD tree: WriteWKDTree: invalid Method "bogus" (want "advanced" or "direct")

--method takes those two.

writing WKD tree: WriteWKDTree: domain "ex/ample.com" contains a path separator

--domain becomes a directory name, so it is validated as a hostname: letters, digits, hyphens and dots, no leading or trailing dot, no .., no separators.

--submission-address auto requires at least one resolvable --email

auto copies the first resolved email. With no --email flags and no email discoverable from the input keys there is nothing to copy.

The key could not be published

site/minisign/demo/v1.pub already holds a different key — published keys are add-only; publish a new generation instead

That project and generation already has a different key published. Consumers pin these, so it cannot be replaced. Publish the new key under a new --generation and re-run the old one with --status retired.

keys.json records a different key for demo generation 1

The same violation caught in the manifest rather than on disk — the .pub file was removed or edited but the manifest entry remains.

got "Demo": project name must be lowercase alphanumeric with dots, dashes or underscores

--project becomes a path segment. It must match ^[a-z0-9][a-z0-9._-]{0,63}$ — start with a lowercase letter or digit, contain only lowercase letters, digits, dots, dashes and underscores, and be at most 64 characters.

got "gone"; valid values are "active", "retired", "revoked": unknown status

Those three states only.

--generation must be 1 or greater, got 0

Generations start at 1.

parsing --valid-from "01-01-2026" as YYYY-MM-DD: …

--valid-from is a plain date, 2026-08-01, not RFC3339 and not a locale format.

See also