Skip to content

Troubleshooting

Jake Paine edited this page Apr 24, 2026 · 5 revisions

Troubleshooting

sops: command not found or SOPS binary error

Every operation that touches secret files calls the sops binary. If it is not on your PATH or configured with a valid explicit path, SOPS-backed commands fail before decrypting or mutating files.

Fix: Install SOPS and verify it works:

sops --version

If SOPS is installed to a non-standard location, point Keyseal to it in keyseal.yaml:

sops:
  binary: /usr/local/bin/sops

Then rerun keyseal doctor.


age binary warning

Doctor warns when sops.age_binary cannot be found or cannot run --version.

This does not fail doctor because SOPS may still be able to encrypt and decrypt depending on your backend. For the default age-based .sops.yaml, install age or point Keyseal at the right binary:

sops:
  age_binary: /usr/local/bin/age

Then rerun:

keyseal doctor

keyseal.yaml not found in <cwd>; run keyseal init first

Every command except init looks for keyseal.yaml in the current working directory. You are either in the wrong directory or the file does not exist.

Fix:

# confirm you are in the right directory
ls keyseal.yaml

# if it does not exist, initialize
keyseal init

not inside a Git repository

The Git-aware commands (status, diff, history, commit, rollback, plus commit-enabled add, edit, and updatekeys) require that your current working directory is inside an existing Git repository.

Fix:

git rev-parse --show-toplevel

If that fails, change into the correct repository or initialize one yourself with git init. Keyseal does not create the Git repository for you.


git binary not found

Keyseal shells out to the git binary for status, diff, history, commit, rollback, and updatekeys --commit behavior.

Fix:

git --version

If Git is installed in a non-standard location, ensure it is available on PATH before you run Keyseal.


Placeholder recipients in .sops.yaml

After keyseal init, the generated .sops.yaml contains:

age: age1REPLACE_ME,age1RECOVERY_REPLACE_ME

SOPS will not encrypt with these values. Doctor will flag them as failures.

Fix: Replace with real age public keys:

# if you need to generate a key pair
age-keygen -o ~/.config/sops/age/keys.txt
# note the public key from the output, then edit .sops.yaml

sops encrypt fails with "no matching creation rules"

SOPS cannot find a creation rule in .sops.yaml that matches the target file path.

Common cause: The path_regex in your creation rule does not match the path you are trying to encrypt. The regex is matched against the final destination path, or the --filename-override value passed by keyseal add.

Example: if your target is production/platform/app.enc.yaml but your rule is:

path_regex: production/platform/.*\.enc\.yaml$

and you have repository.root: ., the path passed to SOPS is relative to where you ran keyseal add. Verify the regex with:

# test the regex against your path
echo "production/platform/app.enc.yaml" | grep -E "production/platform/.*\.enc\.yaml$"

Plaintext file at an .enc.yaml path

Doctor will report:

[fail] production/platform/app.enc.yaml: is non-empty plaintext at an encrypted path

This means the file at that path contains non-empty content but has no SOPS metadata. Keyseal treats that as a plaintext-at-encrypted-path failure even if the content is malformed, because non-empty plaintext secret content must not be ignored.

How this happens: The file was written manually or copied from an example without going through keyseal add or sops encrypt.

Fix:

# remove the plaintext file
rm production/platform/app.enc.yaml

# recreate it properly
keyseal add production/platform/app

# then edit to set real values
keyseal edit production/platform/app

Do not commit a plaintext .enc.yaml file to the repository.


Empty or whitespace-only .enc.yaml placeholder file

Doctor will report a warning and skip decrypt validation for an empty or whitespace-only .enc.yaml file.

This is meant for uninitialized placeholder files that exist in the tree but have not been populated yet.

keyseal render and keyseal exec will skip these files when other requested secrets are usable. If every requested secret is an empty placeholder, the command fails with a clear error instead of silently producing empty output.

keyseal edit <logical-name> can bootstrap the empty file with an encrypted starter document and then open it in SOPS.


render refuses to write because mode is unsafe

Error: mode 0644 is not owner-only; use --force to override

The file mode you specified (or the default in keyseal.yaml) would give read access to group or world users.

Fix: Use 0600 (the default):

keyseal render production/platform/app --out /run/secrets/app.env --mode 0600

Or if you genuinely need a less restrictive mode for your deployment:

keyseal render production/platform/app --out ./app.env --mode 0644 --force

render or exec fails with schema validation error

Keyseal validates the decrypted secret document after every decrypt. Common causes:

  • A key in values does not match validation.key_pattern (default requires uppercase + digits + underscores only). This is usually a key with a lowercase letter or a hyphen.
  • The values map is empty and validation.require_values is true.
  • The document has version: 0 or is missing version.

Fix: Run keyseal edit <logical-name> to fix the document, or adjust validation.key_pattern in keyseal.yaml if lowercase keys are intentional.


keyseal add fails with "unknown template"

Error: unknown template "my-template"

Only four templates exist: laravel, stripe, mail, mysql-app. Template names are case-sensitive.

Fix: Check the available templates in Templates or omit --template to use the default single-key starter.


keyseal add fails with "file already exists"

The target .enc.yaml file already exists and you did not pass --force.

Fix:

# overwrite the existing file
keyseal add production/platform/app --force

# or just edit the existing file
keyseal edit production/platform/app

nothing to commit

keyseal commit returns this when no Keyseal-managed files currently differ from Git history.

The same message can appear on edit --commit, rollback --commit, or updatekeys --commit when the command completed but did not leave any change to stage.

Fix: Check the filtered status view first:

keyseal status

If the file is unchanged, there is nothing for Keyseal to commit.


target file has local changes

keyseal rollback refuses to overwrite a secret file that already has staged or unstaged local changes.

Fix: Either commit or discard the local change first, then retry the rollback.

To inspect the target rollback safely:

keyseal rollback production/platform/app --to <commit> --dry-run

--dry-run is preview-only and does not require the target file to be clean.


keyseal doctor exits 1 with no visible error

Doctor exits 1 when any check has status fail. The failure is printed to stdout, not stderr. If you are running in a context that suppresses stdout, redirect it:

keyseal doctor 2>&1
# or use JSON to separate structured output
keyseal doctor --json

Logical name validation errors

Error: invalid logical name "production/platform/app.enc.yaml": name must not end with .yaml

Common logical name mistakes:

Bad input Problem
production/platform/app.enc.yaml Do not include the file extension
/production/platform/app Logical names must be relative
production/../platform/app No path traversal
production//platform/app No empty segments

Build produces version dev

If you build without make build (e.g. go build ./cmd/keyseal), the ldflags that inject version metadata are not set. The binary will report keyseal dev.

Fix: Always use make build or make dist for any build you care about the version string of.


Release archive extraction issues

Each release archive contains the binary, README.md, and LICENSE in a top-level directory named after the archive. Extract with:

tar xzf keyseal_v0.1.0_linux_amd64.tar.gz
# produces: keyseal_v0.1.0_linux_amd64/keyseal

Verify the checksum before using the binary:

sha256sum -c keyseal_v0.1.0_checksums.txt --ignore-missing

SOPS decryption fails with "no key was able to decrypt"

Your age private key is not at the location SOPS expects, or the file was encrypted with a different key.

Keyseal resolves the age key path in this order:

  1. SOPS_AGE_KEY_FILE from the environment
  2. sops.age_key_file from keyseal.yaml
  3. SOPS default lookup

Check:

# check the env var
echo $SOPS_AGE_KEY_FILE

# inspect the configured fallback path
grep -A2 '^sops:' keyseal.yaml

# try decrypting directly with sops to isolate the issue
sops --decrypt production/platform/app.enc.yaml

Doctor will report the SOPS error output (up to 3 lines) when decryption fails.

Clone this wiki locally