Troubleshooting¶
Common issues and how to resolve them.
Known Issues¶
- Partial encryption:
envdrift lockcould encrypt combined files (like.env.production) and make non-sensitive values unreadable. Fix: lock now skips.clearand combined files; useenvdrift pushfor partial encryption workflows on older versions.
Schema Validation Issues¶
"Module not found" when validating¶
Problem:
Solution:
- Check the module path is correct (use
:to separate module from class) - Use
--service-dirto add the correct directory to Python path
# Wrong
envdrift validate .env --schema config.settings.Settings
# Correct
envdrift validate .env --schema config.settings:Settings
# With service directory
envdrift validate .env --schema config:Settings --service-dir ./backend
Settings class instantiates on import¶
Problem:
This happens when your settings module creates a Settings() instance at import time.
Solution:
Guard the instantiation:
import os
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
DATABASE_URL: str
# Guard against import during schema extraction
if not os.getenv("ENVDRIFT_SCHEMA_EXTRACTION"):
settings = Settings()
"Extra fields not permitted"¶
Problem:
Solution:
Either add the variable to your schema or change the extra setting:
class Settings(BaseSettings):
model_config = SettingsConfigDict(
extra="ignore" # or "allow" to silence the warning
)
Encryption Issues¶
"dotenvx not found"¶
Problem:
Solution:
Install dotenvx:
# macOS
brew install dotenvx/brew/dotenvx
# npm
npm install -g @dotenvx/dotenvx
# Or enable auto-install in config
"sops not found"¶
Problem:
Solution:
Install SOPS:
# macOS
brew install sops
# Linux. Simplest option: let envdrift install and verify sops for you. It
# reads the pinned version from constants.json and checks the published SHA256
# before installing. Enable it in envdrift.toml:
#
# [encryption.sops]
# auto_install = true
#
# Manual alternative. Runs in a subshell so a failure cannot close your
# terminal and the cleanup trap does not outlive the block.
(
# Fail-closed: `set -e` aborts when `grep` finds no checksum line for this
# asset, and `test -s` below catches an empty extraction. Extracting to a
# file rather than piping into `sha256sum -c` keeps that failure attributable
# — a renamed or wrong-arch asset stops here with an obvious cause instead of
# inside the verifier. `pipefail` guards any pipeline added here later.
set -euo pipefail
# envdrift may be isolated (the installer uses ~/.envdrift/venv, and
# pipx/uv tool installs are isolated too), so pick an interpreter that can
# actually import it rather than assuming a bare `python` on PATH.
for PY in "$HOME/.envdrift/venv/bin/python" python3 python; do
command -v "$PY" >/dev/null 2>&1 || continue
"$PY" -c "import envdrift" 2>/dev/null && ENVDRIFT_PY="$PY" && break
done
: "${ENVDRIFT_PY:?cannot find a Python that can import envdrift - run this from the environment where envdrift is installed}"
SOPS_VERSION=$("$ENVDRIFT_PY" -c "import json,importlib.resources as r; \
print(json.loads(r.files('envdrift').joinpath('constants.json').read_text())['sops_version'])")
: "${SOPS_VERSION:?could not read sops_version from constants.json}"
# Match the machine: installing an x86-64 binary on aarch64 downloads and
# verifies fine, then fails at exec time.
case "$(uname -m)" in
x86_64|amd64) ARCH=amd64 ;;
aarch64|arm64) ARCH=arm64 ;;
*) echo "unsupported architecture: $(uname -m)" >&2; exit 1 ;;
esac
BASE="https://github.com/getsops/sops/releases/download/v${SOPS_VERSION}"
ASSET="sops-v${SOPS_VERSION}.linux.${ARCH}"
TMP=$(mktemp -d)
# Stage on the TARGET filesystem so the final step is a same-filesystem
# rename. `install` alone copies into place and can leave a partial binary if
# interrupted or if the disk fills.
STAGE=/usr/local/bin/.sops.staged.$$
trap 'rm -rf "$TMP"; sudo rm -f "$STAGE"' EXIT
# Bounded so a stalled connection cannot hang the install. Values match
# install_integrity.py (FETCH_TIMEOUT_SECONDS / DOWNLOAD_TIMEOUT_SECONDS);
# note --max-time is a TOTAL time cap for the request, not a per-read one.
CURL="curl -fsSL --connect-timeout 30 --max-time 60"
$CURL -o "$TMP/$ASSET" "$BASE/$ASSET"
$CURL -o "$TMP/checksums.txt" "$BASE/sops-v${SOPS_VERSION}.checksums.txt"
# Verify BEFORE installing, and fail on a MISSING line as loudly as on a
# mismatched one: `grep` exits non-zero when the asset is absent from the
# checksums file (renamed upstream, wrong arch), and `test -s` covers the
# case where it matched but produced nothing usable.
grep " ${ASSET}\$" "$TMP/checksums.txt" > "$TMP/expected.txt"
test -s "$TMP/expected.txt"
( cd "$TMP" && sha256sum -c expected.txt )
sudo install -m 0755 "$TMP/$ASSET" "$STAGE"
sudo mv -f "$STAGE" /usr/local/bin/sops
)
"Failed to decrypt: no key found"¶
Problem:
Causes and solutions:
- Missing .env.keys file — Run
envdrift syncto fetch keys from vault - Wrong key — The file was encrypted with a different key
- SOPS: Missing age key — Set
SOPS_AGE_KEY_FILEor add key to~/.config/sops/age/keys.txt
# For dotenvx
envdrift sync # Fetches keys from vault
# For SOPS with age
export SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt
"File is not encrypted"¶
Problem:
Solution:
The file wasn't encrypted. Run encrypt:
Vault Issues¶
Azure: "DefaultAzureCredential failed"¶
Problem:
Solutions:
- Login with Azure CLI:
az login - Set environment variables:
- Check you have access to the Key Vault in Azure portal
AWS: "Unable to locate credentials"¶
Problem:
Solutions:
- Configure AWS CLI:
aws configure - Set environment variables:
- In CI, use OIDC or IAM roles
HashiCorp: "permission denied"¶
Problem:
Solutions:
- Check your token has the correct policies
- Ensure the token hasn't expired
- Verify the secret path is correct — it is relative to the KV v2 mount,
with no
secret/data/prefix. The client inserts thedata/segment internally, so adding the prefix yourself produces a wrong path.
GCP: "Could not automatically determine credentials"¶
Problem:
Solutions:
- Login with gcloud:
gcloud auth application-default login - Set service account key:
"Secret not found"¶
Problem:
Solutions:
- Check the secret name matches exactly (case-sensitive)
- Verify the secret exists: check vault UI or CLI
- Push the secret first:
envdrift vault-push . myapp-key --env production - To pull a single secret back without a sync config, use
envdrift vault-pull . myapp-key --env production
Pre-commit Hook Issues¶
Hook doesn't run¶
Problem:
Pre-commit skips envdrift hooks.
Solution:
Ensure the hook is properly configured:
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: envdrift-validate
name: Validate .env files
entry: envdrift validate
args: [".env.production", "--schema", "config:Settings", "--ci"]
language: system
files: ^\.env\.production$
pass_filenames: false
"envdrift: command not found" in pre-commit¶
Problem:
Pre-commit can't find envdrift.
Solution:
Use the full path or ensure it's in PATH:
CI/CD Issues¶
Validation passes locally but fails in CI¶
Causes:
- Different Python version — Schema behaves differently
- Missing dependencies — Install with
pip install envdrift[vault] - Different working directory — Use absolute paths or
--service-dir
Exit code is always 0¶
Problem:
Pipeline doesn't fail on validation errors.
Solution:
Add --ci flag:
Can't decrypt in CI¶
Problem:
Solutions:
- Store the private key as a CI secret
- Use vault sync with CI credentials
- For SOPS, configure KMS access for the CI runner
# GitHub Actions example
- name: Decrypt env
env:
DOTENV_PRIVATE_KEY_PRODUCTION: ${{ secrets.DOTENV_PRIVATE_KEY_PRODUCTION }}
run: |
echo "DOTENV_PRIVATE_KEY_PRODUCTION=$DOTENV_PRIVATE_KEY_PRODUCTION" > .env.keys
envdrift decrypt .env.production
Performance Issues¶
Slow schema loading¶
Problem:
envdrift validate takes a long time to start.
Causes:
- Heavy imports in your settings module
- Database connections at import time
Solution:
Guard expensive operations:
import os
if not os.getenv("ENVDRIFT_SCHEMA_EXTRACTION"):
# Expensive imports or operations here
from myapp.database import engine
Getting Help¶
If you're still stuck:
- Run with
--verbosefor more details - Check the FAQ
- Search GitHub Issues
- Open a new issue with reproduction steps