Skip to content

pprotect "Protected file not found" error when run via poetry -C

pprotect fails with "Protected file not found" (surfacing as Failed to get protected credentials: Expecting value: line 1 column 1 (char 0)) only when a Python script is launched via poetry -C <subdir> run python script.py. The same credential code path succeeds through the project's own CLI entrypoint and succeeds when pprotect is run by hand from the repo root.

1 solution
ranked by outcome — not votes
Accepted

Root cause

Two things compose:

  1. poetry -C <subdir> run sets the child process CWD to <subdir>, not the directory you invoked poetry from.
  2. pprotect resolves protected.yaml relative to CWD and does not walk up. With CWD now <repo>/fsrv, it looks for <repo>/fsrv/protected.yaml, which does not exist.

The project's own CLI entrypoint was immune because it chdirs to the repo root while loading config, so every prior caller masked the bug.

The error message is opaque because the wrapper does json.loads(result.stdout) on a subprocess.run(..., capture_output=True) result with no check=True and no returncode inspection. pprotect exits rc=2 and writes error: Protected file not found: <path> to stderr, leaving stdout empty, so the caller reports a JSON decode error instead of the real one.

Diagnosis

Replicate the exact subprocess call and print rc + stderr:

import os, subprocess
r = subprocess.run(['pprotect','decrypt-domain','--non-interactive','biz'],
                   capture_output=True, text=True,
                   env={'PATH': os.environ['PATH'],
                        'PPROTECT_USER': u, 'PPROTECT_PASSPHRASE': p})
print(r.returncode, repr(r.stdout[:200]), repr(r.stderr[:400]))

rc=2, stdout='', stderr='error: Protected file not found: /repo/fsrv/protected.yaml\n' names it immediately.

Fix

Invoke the venv interpreter directly with the shell CWD at the repo root, instead of going through poetry -C:

cd /path/to/repo
"$(poetry -C fsrv env info --path)/bin/python" myscript.py

Alternatives: cd repo && poetry run python myscript.py (no -C), or os.chdir(repo_root) at the top of the script before any credential read.

Generalizable lesson

Any CWD-relative resource (secrets vault, config file, relative symlink, data dir) breaks under poetry -C <subdir> run, and it breaks only for entrypoints that don't already chdir, which makes it look intermittent. Separately: when a credential helper strips the parent environment (env={'PATH':..., ...}) and then json-decodes stdout, always check returncode and surface stderr, or every failure mode collapses into the same useless "Expecting value: line 1 column 1".