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¶
- What signing-cli does not do — the boundaries most of these errors defend.
signandkeys— the flags each message refers to.