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 gitlab.com/phpboyscout/go/errors, so errors.Is works
through the wrapping. Failures that originate upstream carry the upstream
sentinels instead: signing.ErrUnknownBackend, signing.ErrUnknownBackendType,
openpgpkey.ErrUnsupportedKeyType, and the local backend's
ErrOpenPGPSecretKey, 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 (see 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,
keys mint and keys minisign exist and can never succeed. Add a blank import to
main.
constructing signer: ./key.priv.asc: unsupported key type: an armored OpenPGP secret key; the local backend reads PEM only¶
--key-id points the local backend at an armored OpenPGP private half
(.priv.asc, the default from keys generate --algorithm ed25519) rather than a
PEM. The backend recognises the armor and refuses it by type. Regenerate with
--private-format pem, or point at the .pem file you already have.
The error carries the hint write the private half as PKCS#8 PEM instead (keys
generate --private-format pem). Whether that line is printed is up to the host
binary. Match it with errors.Is(err, local.ErrOpenPGPSecretKey).
constructing signer: ./key.txt: no PEM block found in file¶
The local backend was handed a file that is neither PEM nor an armored OpenPGP
secret key. Check that --key-id names the private key file you meant.
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.
A named instance was refused¶
These come from the --backend selector on sign, keys mint and
keys minisign when it names an instance (<type>:<instance>) or carries
settings. The grammar is in Selecting a backend.
Every one is raised before any file is read or any backend is contacted. None of
them repeats a setting's value.
--kms-region configures the default "aws-kms" backend, not the named instance "aws-kms:prod"; pass it as a selector setting instead: --backend 'aws-kms:prod?kms-region=…'¶
A top-level backend flag was set alongside a named instance. That flag is bound
to the registered default, so it would have had no effect on aws-kms:prod.
Move the value into the selector as the message shows.
backend "local": settings (?…) are only valid on a named instance, e.g. "local:NAME?…"¶
Settings were given on a bare type. Name an instance: local:ci?….
backend "aws-kms:Prod": illegal instance name "Prod" (allowed: ^[a-z0-9][a-z0-9-]*$)¶
Instance names are lowercase letters, digits and hyphens, starting with a letter
or digit. An empty name (aws-kms:) fails the same way.
backend "local:prod" accepts no settings¶
The backend declares no flags, so there is nothing a setting could configure.
Drop the ?… part.
backend "aws-kms:prod": unknown setting "bogus"¶
Every key must be a flag the backend declares, written without --. Check
--help for the backend's flags.
backend "aws-kms:prod": duplicate setting "kms-region"¶
Each key may appear once.
backend "aws-kms:prod": setting "kms-region" needs a non-empty value¶
kms-region= with nothing after it. Give a value or drop the setting to keep the
flag's default.
backend "<type>:<instance>": setting "<key>" is not a valid <flag-type>¶
The backend's flag rejected the value, for instance a word where it declares an
int. The last word is the flag's type as the backend declares it. The setter's
own reason is left out, because it usually repeats the value. Neither local nor
aws-kms declares a flag that can reject a value (kms-region takes any text),
so this only comes from a backend with typed flags.
backend "aws-kms:prod": setting 2 has no '=' (write it as key=value)¶
A setting with no =, such as a bare kms-region or a value typed without its
key. The message counts settings from 1, left to right, to say which one it
means, and does not quote it, because what was typed may be a value.
backend "aws-kms:prod": setting 2 has an empty key (nothing before its '=')¶
A setting written as =value. The message counts settings from 1, left to right,
to say which one it means, and leaves the value out.
backend "aws-kms:prod": empty setting (a leading, trailing or doubled '&')¶
An & with nothing on one side of it.
backend "aws-kms:prod": bad value for setting "kms-region": malformed %-escape (write a literal % as %25)¶
Settings are url-decoded, so a % must start a valid escape. The message does
not show which escape was malformed, because that would repeat part of the
value.
backend "aws-kms:prod": setting 2 has a malformed %-escape in its key (write a literal % as %25)¶
The same, in a key. The message counts settings from 1, left to right, to say which one it means, and does not quote the key, because what was typed there may be a value.
"nope": unknown signing backend type¶
The type before the colon is not compiled into this binary, or it is and offers no named instances. When the type is compiled in as a default, the message is replaced by the next one.
backend "aws-kms" is registered but this build offers no named instances for it (no factory) — use --backend "aws-kms" for the default, or update the backend module to a version that registers a factory¶
The binary has the backend but was built against a version that predates named
instances (aws-kms gained them in signing-aws-kms v0.6.0). Use the bare type,
or update the backend module.
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: public key embeds *eddsa.PublicKey but signer is RSA¶
sign --format openpgp was given an RSA --key-id and an Ed25519 --public-key,
for example the .asc from keys generate --algorithm ed25519. Point
--public-key at the .asc that belongs to the RSA key.
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, keys mint and
keys minisign 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¶
sign --output pointed at an existing directory. The keys subcommands report
the same mistake as output file already exists without --force, and with it as
opening "keys-site" for writing: open keys-site: is a 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: a different key is already published at this path¶
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.
site/keys.json records a different key for demo generation 1: a different key is already published at this path¶
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.
The command was not recognised¶
unknown command "bogus" for "mytool keys": unknown subcommand¶
keys was given a word that is not one of its five subcommands. The quoted path
is your binary's own. Run keys --help for the list.
See also¶
- What signing-cli does not do: the boundaries most of these errors defend.
signandkeys: the flags each message refers to.