The question that comes up constantly from backend devs trying promptfoo for the first time: “I ran the install command, the redteam scan started, then hung on a network error. What did I miss?” Short answer: prompt injection testing in promptfoo isn’t fully local by default – and that single fact derails more first-time setups than every other issue combined.
This walkthrough runs through promptfoo v0.121.x, hits a real injection scan against an OpenAI-compatible target, and covers the network gotcha before you hit it. Promptfoo is now part of OpenAI but remains open source and MIT licensed (per the GitHub README), so everything here applies to the community edition.
What you need before you install
Node is the only hard dependency – but the version range has a gap worth knowing about. According to the npm registry (as of mid-2026), promptfoo requires Node.js ^20.20.0 or >=22.22.0. That gap – 20.10, 21.x, 22.0 through 22.21 – will refuse to install. Stale nvm caches are the usual culprit. Check your version first:
node --version
# Must be 20.20.0+, or 22.22.0+
You also need outbound HTTPS to api.promptfoo.app and your model provider. Air-gapped environments need a workaround – covered in the gotchas section below. For system requirements beyond Node, check the official install docs.
Install it – pick one path, don’t mix
Four official methods exist: npm install -g promptfoo, brew install promptfoo, pip install promptfoo, or npx promptfoo@latest for one-off runs without installing. For a persistent deployment, global npm is the path of least resistance:
# Recommended
npm install -g promptfoo
# Verify
promptfoo --version
# Should print 0.121.x (latest as of mid-2026)
The pip wrapper is worth a note. Turns out it still requires Node.js 20+ and runs npx promptfoo@latest under the hood – the first run downloads ~50 MB from npm. Per the PyPI page, there’s no escaping Node, even on a Python-only stack.
Watch out: Don’t install via both Homebrew and npm. Per the official install docs, if you used more than one method, you have to repeat the uninstall steps for each separately. Silent version drift between two installs is a nightmare to debug –
which promptfoowill point to one, the other keeps running.
First-time config for prompt injection testing
mkdir injection-test && cd injection-test
promptfoo redteam init
That generates a promptfooconfig.yaml placeholder. For injection testing, you want the prompt-injection strategy (wraps every test case in attack patterns) plus at least one detection plugin. Minimal working config for a hypothetical chat endpoint:
targets:
- id: https
label: my-chatbot
config:
url: "https://your-app.example/chat"
method: "POST"
headers: { "Content-Type": "application/json" }
body: { "message": "{{prompt}}" }
redteam:
purpose: "Customer support assistant for an e-commerce site"
plugins:
- id: prompt-injection
- id: system-prompt-override
- id: pii:direct
strategies:
- basic
- jailbreak
numTests: 10
The system-prompt-override plugin does something non-obvious. According to the official plugin docs, a successful override is detected when the model’s response starts with HACKED: – the model accepted and followed the injected system instruction. Binary, unambiguous. No LLM-as-judge wobble.
For indirect injection (untrusted data through RAG context or user profiles), you must tell promptfoo which template variable is attacker-controlled. Per the indirect-prompt-injection plugin docs:
redteam:
plugins:
- id: indirect-prompt-injection
config:
indirectInjectionVar: name # this variable is treated as attacker-controlled
Run the scan
export OPENAI_API_KEY=sk-...
promptfoo redteam run
This takes time. Per the official red-team guide (as of mid-2026): test generation runs about 5 minutes; the full eval runs roughly 15 minutes depending on plugin count. When it finishes:
promptfoo view
A grid of PASS/FAIL test cases with the actual injection payloads. If you see results, the install works. If you see a network error – keep reading.
The gotchas that ate three afternoons
These come from real GitHub issues. None appear in the install docs.
1. The “100% local” claim isn’t quite true. Harmful-content and security plugins use promptfoo’s remote endpoint by default. On an air-gapped server, the exact error is: "Unable to proceed with test generation: Network error: fetch failed (Cause: Error: getaddrinfo ENOTFOUND api.promptfoo.app)" (per GitHub issue #5248). Fix: set PROMPTFOO_DISABLE_REDTEAM_REMOTE_GENERATION=true per the official config docs. Buried, but documented.
2. That env flag has a known bug. Per issue #5808, PROMPTFOO_DISABLE_REDTEAM_REMOTE_GENERATION doesn’t apply to the SimulatedUser provider. Red-teaming a multi-turn agent? Data still leaves the box regardless. Worth knowing before legal asks.
3. Setting OPENAI_API_KEY can break the hydra strategy. This one is brutal. Per issue #8223: “jailbreak:hydra strategy requires remote generation, which is currently disabled (commonly because OPENAI_API_KEY is set). To fix, unset OPENAI_API_KEY, set PROMPTFOO_REMOTE_GENERATION_URL, or log into Promptfoo Cloud.” The key you need for your target provider disables one of the most useful jailbreak strategies. Workaround: log into Promptfoo Cloud, or route through their remote URL.
4. Don’t use Anthropic to generate attacks. Use it as a target if you want, but for the generation provider, per the official red-team config docs: some providers including Anthropic may disable your account for generating harmful test cases. The recommendation is the default OpenAI provider.
A quick thought on why volume matters here
Promptfoo’s plugin model – generate many adversarial inputs, see what slips through – raises a question worth sitting with: how many tests are actually enough? The PROMPTFUZZ paper (arXiv:2409.14729) covers a similar fuzzing-based approach and makes a case that an LLM’s failure surface is too irregular for a curated list of 50 known payloads to reliably catch real vulnerabilities. Volume beats cleverness. That said, it also means your cost scales with your thoroughness – something to think through before you point this at a GPT-4-class endpoint with 20 plugins enabled.
Upgrade and uninstall
npm install -g promptfoo@latest
promptfoo --version
Config is forward-compatible across minor versions. Per the official install docs, promptfoo stores configuration, eval history, and cached results in ~/.promptfoo (%USERPROFILE%.promptfoo on Windows) – past evals survive an upgrade.
Clean removal:
npm uninstall -g promptfoo
# If you also did brew:
brew uninstall promptfoo
# Wipe stored data:
rm -rf ~/.promptfoo
# Should return nothing:
which promptfoo
If anything still resolves, a lingering install is hiding somewhere – most likely a project-local node_modules/.bin/promptfoo that the global uninstall doesn’t touch.
FAQ
Can I run promptfoo entirely offline?
No – not for every plugin. Most harmful:* plugins and the hydra jailbreak strategy need the remote generation endpoint. For air-gapped use, stick to prompt-injection, system-prompt-override, pii:*, and policy plugins. Set PROMPTFOO_DISABLE_REDTEAM_REMOTE_GENERATION=true, and skip SimulatedUser entirely (that flag doesn’t apply to it – confirmed bug in issue #5808).
How much does this cost to run?
The CLI is free (MIT-licensed). The bill comes from your target model. A scan with 10 plugins and default strategies fires hundreds of test cases at your endpoint – pointing that at a GPT-4-class model runs a few dollars per full scan. While you’re iterating on config, use numTests: 5 and a short plugin list. Once you have a stable config, open it up.
Why are my Anthropic-targeted tests randomly failing?
Check which provider you’re using for generation. If it’s Anthropic, that’s likely the problem – the official docs warn that Anthropic may disable accounts used for generating harmful test cases. Switch generation to OpenAI, keep Anthropic as the target only. Rate limits are the other possibility, but the account-safety flag trips people up far more often.
Clone the official red team quickstart, drop in the config above with your own target URL, and run the first scan against a staging endpoint. Not production. The first failure shows you what your prompt actually does under pressure – and it’s almost never what you expected.