Skip to content

Troubleshooting

Common issues and how to resolve them.

Known Issues

  • Partial encryption: envdrift lock could encrypt combined files (like .env.production) and make non-sensitive values unreadable. Fix: lock now skips .clear and combined files; use envdrift push for partial encryption workflows on older versions.

Schema Validation Issues

"Module not found" when validating

Problem:

Error: Cannot import module 'config.settings'

Solution:

  1. Check the module path is correct (use : to separate module from class)
  2. Use --service-dir to 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:

ValidationError: DATABASE_URL field required

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:

EXTRA VARIABLES (not in schema):
  * EXTRA_VAR

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:

Error: dotenvx is not installed

Solution:

Install dotenvx:

# macOS
brew install dotenvx/brew/dotenvx

# npm
npm install -g @dotenvx/dotenvx

# Or enable auto-install in config
# envdrift.toml
[encryption.dotenvx]
auto_install = true

"sops not found"

Problem:

Error: sops is not installed

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:

Error: could not decrypt: no key found to decrypt

Causes and solutions:

  1. Missing .env.keys file — Run envdrift sync to fetch keys from vault
  2. Wrong key — The file was encrypted with a different key
  3. SOPS: Missing age key — Set SOPS_AGE_KEY_FILE or 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:

Warning: File does not appear to be encrypted

Solution:

The file wasn't encrypted. Run encrypt:

envdrift encrypt .env.production

Vault Issues

Azure: "DefaultAzureCredential failed"

Problem:

azure.core.exceptions.ClientAuthenticationError: DefaultAzureCredential failed

Solutions:

  1. Login with Azure CLI: az login
  2. Set environment variables:
export AZURE_CLIENT_ID="..."
export AZURE_CLIENT_SECRET="..."
export AZURE_TENANT_ID="..."
  1. Check you have access to the Key Vault in Azure portal

AWS: "Unable to locate credentials"

Problem:

botocore.exceptions.NoCredentialsError: Unable to locate credentials

Solutions:

  1. Configure AWS CLI: aws configure
  2. Set environment variables:
export AWS_ACCESS_KEY_ID="..."
export AWS_SECRET_ACCESS_KEY="..."
  1. In CI, use OIDC or IAM roles

HashiCorp: "permission denied"

Problem:

hvac.exceptions.Forbidden: permission denied

Solutions:

  1. Check your token has the correct policies
  2. Ensure the token hasn't expired
  3. Verify the secret path is correct — it is relative to the KV v2 mount, with no secret/data/ prefix. The client inserts the data/ segment internally, so adding the prefix yourself produces a wrong path.
export VAULT_TOKEN="hvs.xxx"
vault kv get secret/myapp/key  # Test access manually

GCP: "Could not automatically determine credentials"

Problem:

google.auth.exceptions.DefaultCredentialsError: Could not automatically determine credentials

Solutions:

  1. Login with gcloud: gcloud auth application-default login
  2. Set service account key:
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account.json"

"Secret not found"

Problem:

Error: Secret 'myapp-key' not found in vault

Solutions:

  1. Check the secret name matches exactly (case-sensitive)
  2. Verify the secret exists: check vault UI or CLI
  3. Push the secret first: envdrift vault-push . myapp-key --env production
  4. 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:

entry: /path/to/venv/bin/envdrift validate
# or
entry: python -m envdrift validate

CI/CD Issues

Validation passes locally but fails in CI

Causes:

  1. Different Python version — Schema behaves differently
  2. Missing dependencies — Install with pip install envdrift[vault]
  3. 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:

envdrift validate .env.production --schema config:Settings --ci

Can't decrypt in CI

Problem:

Error: No key found to decrypt

Solutions:

  1. Store the private key as a CI secret
  2. Use vault sync with CI credentials
  3. 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:

  1. Heavy imports in your settings module
  2. 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:

  1. Run with --verbose for more details
  2. Check the FAQ
  3. Search GitHub Issues
  4. Open a new issue with reproduction steps