Skip to content

fingerprint_client: read the key from .env, like everything else does - #27

Merged
jehrr merged 1 commit into
mainfrom
fingerprint-reads-dotenv
Sep 11, 2026
Merged

fingerprint_client: read the key from .env, like everything else does#27
jehrr merged 1 commit into
mainfrom
fingerprint-reads-dotenv

Conversation

@jehrr

@jehrr jehrr commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

--key defaulted to os.environ.get("TWOCAPTCHA_KEY") alone, so a key put
in .env — exactly as §3, the README and .env.example instruct — worked
for every engine and failed here with "No API key". A documented
mechanism not applied on one path, which is the shape of half the defects
§16 lists.

Found on a sibling repo's first live --fingerprint run, then checked
across the family before patching
, per §16: five repos had it, one
(catawiki) had already fixed it. This is that fix, made identical everywhere.

Why env_value and not os.environ.get

Measured both ways with TWOCAPTCHA_KEY=your_2captcha_api_key_here exported:

What the user sees
os.environ.get Fingerprint API rejected the key (401) — note this is a separate subscription from captcha solving
env_value TWOCAPTCHA_KEY is still set to the placeholder from .env.example — treating it as unset

The first sends the reader off to check a subscription they never needed.

load_env() is called here rather than relied upon, because this is a
standalone entry point that no engine has necessarily run first.

Pinned

A new check, verified to fail on the old code by reverting: that it loads
.env itself, that it reads through the loader, that it does not read
os.environ directly, that a placeholder still reads as unset, and that the
help string does not interpolate its default — which is one substring away
from printing a live credential to anyone who types --help.

Offline suite green, --help works, ci_checks.py --all passes.

🤖 Generated with Claude Code

`--key` defaulted to `os.environ.get("TWOCAPTCHA_KEY")` alone, so a key put
in `.env` — exactly as §3, the README and .env.example instruct — worked for
every engine and failed HERE with "No API key". A documented mechanism not
applied on one path, which is the shape of half the defects §16 lists. Found
on a sibling repo's first live --fingerprint run, then checked across the
family before patching: five repos had it, one had already fixed it.

The fix reads through `env_config.env_value` rather than `os.environ.get`,
and that choice is measured rather than stylistic. With
TWOCAPTCHA_KEY=your_2captcha_api_key_here exported:

  os.environ.get   sends the placeholder to the API; the run reports
                   "Fingerprint API rejected the key (401) — note this is a
                   separate subscription", sending the reader off to check a
                   subscription they never needed
  env_value        "TWOCAPTCHA_KEY is still set to the placeholder from
                   .env.example — treating it as unset"

`load_env()` is called here rather than relied upon, because this is a
standalone entry point that no engine has necessarily run first.

Pinned by a check that was verified to FAIL on the old code: that it loads
.env itself, that it reads through the loader, that it does NOT read
os.environ directly, that a placeholder still reads as unset, and that the
help string does not interpolate the default — which is one substring away
from printing a live credential to anyone who types --help.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread fingerprint_client.py
Comment on lines +347 to 352
# filled the key in. `env_value` says so instead, by name.
env_config.load_env()
p.add_argument("--key", default=env_config.env_value("TWOCAPTCHA_KEY"),
help="API key. Defaults to TWOCAPTCHA_KEY from the "
"environment or .env (safer than argv).")
# Measured against the live API on 2026-09-09, because the example this

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

env_config.env_value("TWOCAPTCHA_KEY") is passed directly as default=, so it's evaluated eagerly the instant add_argument() runs — before parse_args() ever sees argv. Since env_value() (env_config.py#L144-L154) logs a warning whenever the value matches a .env.example placeholder, this means:

  • A bare --help invocation logs the placeholder warning even though no key resolution is actually happening.
  • A user who has the placeholder in .env but correctly passes --key sk_real... on the command line still gets the spurious "is still set to the placeholder... treating it as unset" warning, even though their explicit flag overrides the default and the placeholder is never actually used.

The sibling scraper_api_client.py avoids this exact problem: it keeps default=os.environ.get("TWOCAPTCHA_KEY") (side-effect-free) and instead calls env_config.apply(args, keys={"TWOCAPTCHA_KEY": "key", ...}) after parse_args() (scraper_api_client.py#L294-L298) — apply() only calls env_value() if the destination is still falsy (env_config.py#L166-L173), so an explicit --key short-circuits before the placeholder check ever runs.

Suggest following that same shape here: keep the default as a plain env read and defer the env_value()/placeholder check to after parse_args(), only applying it if args.key is still unset.

@jehrr
jehrr merged commit a999800 into main Sep 11, 2026
6 checks passed
@jehrr
jehrr deleted the fingerprint-reads-dotenv branch September 11, 2026 20:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant