401/Auth Failure: Check auth_mode First, Then Headers
What You'll Learn
- Determine within 3 minutes whether 401 is blocked by Antigravity Tools auth middleware
- Understand the "effective value" of the four
proxy.auth_modemodes (especiallyauto) in your current configuration - Get requests through with the correct API Key Header (and avoid Header priority pitfalls)
Your Current Struggles
Your client receives a 401 Unauthorized error when calling the local reverse proxy:
- Python/OpenAI SDK: throws
AuthenticationError - curl: returns
HTTP/1.1 401 Unauthorized - HTTP clients: response status code 401
What is 401/Auth Failure?
401 Unauthorized in Antigravity Tools most commonly means: the proxy has authentication enabled (determined by proxy.auth_mode), but the request either doesn't carry the correct API Key, or carries a higher-priority but mismatching Header, causing auth_middleware() to directly return 401.
First confirm whether "proxy is blocking you"
Upstream platforms may also return 401, but this FAQ only handles "401 caused by proxy authentication". You can quickly distinguish this with /healthz below.
Quick Troubleshooting (Follow This Order)
Step 1: Use /healthz to Determine "Whether Auth is Blocking You"
Whyall_except_health allows /healthz through but blocks other routes; this helps you quickly locate whether 401 comes from the proxy authentication layer.
# Without any auth Header
curl -i http://127.0.0.1:8045/healthzYou should see
- When
auth_mode=all_except_health(orautowithallow_lan_access=true): usually returns200 - When
auth_mode=strict: returns401
/healthz is GET at the routing layer
The proxy registers GET /healthz in routes (see src-tauri/src/proxy/server.rs).
Step 2: Confirm the "Effective Value" of auth_mode (Especially auto)
Whyauto is not an "independent policy"—it calculates the actual mode to execute based on allow_lan_access.
proxy.auth_mode | Additional Condition | Effective Mode |
|---|---|---|
off | - | off |
strict | - | strict |
all_except_health | - | all_except_health |
auto | allow_lan_access=false | off |
auto | allow_lan_access=true | all_except_health |
You can check this in the GUI's API Proxy page: Allow LAN Access and Auth Mode.
Step 3: Confirm api_key is Not Empty and You're Using the Same Value
Why When auth is enabled, if proxy.api_key is empty, auth_middleware() will directly reject all requests and log an error.
Proxy auth is enabled but api_key is empty; denying requestYou should see
- In the API Proxy page, you can see a key starting with
sk-(default value is auto-generated byProxyConfig::default()) - After clicking "Regenerate/Edit" and saving, external requests immediately validate with the new key (no restart needed)
Step 4: Try with the Simplest Header First (Don't Use Complex SDKs Yet)
Why The middleware reads Authorization first, then x-api-key, then x-goog-api-key. If you send multiple Headers at once and the first one is wrong, even if the later ones are correct, they won't be used.
# Recommended format: Authorization + Bearer
curl -i http://127.0.0.1:8045/v1/models \
-H "Authorization: Bearer sk-REPLACE_WITH_YOUR_PROXY_API_KEY"You should see: HTTP/1.1 200 OK (or at least no longer 401)
Proxy Authorization compatibility details
auth_middleware() strips the Authorization value once using the Bearer prefix; if there's no Bearer prefix, it uses the entire value as the key for comparison. Documentation still recommends Authorization: Bearer <key> (more compliant with common SDK conventions).
If you must use x-api-key:
curl -i http://127.0.0.1:8045/v1/models \
-H "x-api-key: sk-REPLACE_WITH_YOUR_PROXY_API_KEY"Common Pitfalls (All Real Issues from Source Code)
| Symptom | Real Cause | How to Fix |
|---|---|---|
auth_mode=auto, but localhost calls still get 401 | allow_lan_access=true causes auto to evaluate to all_except_health | Disable allow_lan_access, or make the client carry the key |
| You think "I clearly sent x-api-key", but still get 401 | Also sent a mismatching Authorization, middleware prioritizes that | Remove extra Headers, keep only one you're sure is correct |
Authorization: Bearer<key> still gets 401 | Missing space after Bearer, cannot strip by Bearer prefix | Change to Authorization: Bearer <key> |
All requests get 401, logs show api_key is empty | proxy.api_key is empty | Regenerate/set a non-empty key in the GUI |
Lesson Summary
- First use
/healthzto locate whether 401 comes from the proxy authentication layer - Then confirm
auth_mode(especiallyauto's effective mode) - Finally send only one definitely correct Header for verification (avoid Header priority pitfalls)
Next Lesson Preview
In the next lesson, we'll learn 429/Quota Errors: Correct Expectations for Account Rotation & Misconceptions About Model Capacity Exhaustion.
You'll learn:
- Whether 429 is "quota shortage" or "upstream rate limiting"
- Correct expectations for account rotation (when it automatically switches and when it doesn't)
- Use configuration to reduce 429 probability
Appendix: Source Code Reference
Click to expand source code locations
Updated: 2026-01-23
| Feature | File Path | Line Numbers |
|---|---|---|
| ProxyAuthMode enum | src-tauri/src/proxy/config.rs | 5-18 |
| ProxyConfig: allow_lan_access/auth_mode/api_key and defaults | src-tauri/src/proxy/config.rs | 174-258 |
| auto mode parsing (effective_auth_mode) | src-tauri/src/proxy/security.rs | 1-30 |
| Auth middleware (Header extraction and priority, /healthz exemption, OPTIONS allow) | src-tauri/src/proxy/middleware/auth.rs | 14-77 |
| /healthz route registration and middleware order | src-tauri/src/proxy/server.rs | 170-193 |
| Auth documentation (modes and client conventions) | docs/proxy/auth.md | 1-45 |
Key Enums:
ProxyAuthMode::{Off, Strict, AllExceptHealth, Auto}: Authentication modes
Key Functions:
ProxySecurityConfig::effective_auth_mode(): Resolvesautoto actual policyauth_middleware(): Executes authentication (includes Header extraction order and /healthz exemption)