Troubleshooting
Every Upwarden-originated error is prefixed [UPWARDEN] and returns structured JSON. If you don’t see that prefix, the failure is upstream (network, registry, or your package-manager config) — not Upwarden.
Unauthorized (HTTP 401)
Section titled “Unauthorized (HTTP 401)”{ "error": "Unauthorized" }REQUIRE_PROXY_AUTHis on and the request carried no key or a bad one. Confirm yourvk_…key is set in the right env var and sent asAuthorization: Bearer …(or Basic, per ecosystem).- The key is scoped to a different project/tenant than the host you’re hitting. One key = one project.
A package you expected is blocked (HTTP 403)
Section titled “A package you expected is blocked (HTTP 403)”The 403 body is a structured JSON envelope. Package managers print the error line; CI / scripts can parse the rest for routing.
{ "error": "[UPWARDEN] Package 'foo@1.2.3' is BLOCKED. Downgrade to known-safe version: 'foo@1.2.2'.", "advisory": { "source": "osv", "id": "GHSA-...-...-...", "cve": "CVE-2024-12345", "severity": "HIGH", "summary": "<one-line explanation of the vulnerability>" }, "remediation": { "safe_version": "1.2.2", "guidance": "<plain-text fix steps>" }}advisory is present when the block is anchored to an OSV / GHSA record; absent for cooldown / oversize / policy blocks. See Verdicts & the scanner → What a 403 carries for the full envelope contract.
Likely causes, most common first:
- Known vulnerability.
advisory.cveis populated, severity will tell you whether to drop everything (CRITICAL/HIGH) or schedule the bump. Theremediation.safe_versionis the highest known-safe version below the blocked one. - Cooldown.
VANGUARD_DEFAULT_MIN_AGE_DAYSrefuses any version younger than N days (closes the freshly-published-malware window). A brand-new legitimate release trips this. Noadvisoryblock. Fix: wait it out, pin an older version, or have your operator lower the cooldown / allow the package. - Build-tool fingerprint. The blocked package is a transitive of a known build tool (esbuild, swc, sass-embedded, …).
remediation.fingerprintcarries the upstream tool name + affected range — bump that tool, not the transitive. See Verdicts → pipeline step 4. - Oversized artifact / size-jump anomaly. A prescan hit the decompressed-size cap, or the tarball is more than 5× the median of its three prior versions (a strong compromise signal — see step 4 of the pipeline). Operator adds
ecosystem:nametoSCANNER_SIZE_ALLOWLIST, or tunesSCANNER_SIZE_JUMP_MULTIPLIER. Don’t allowlist a size-jump without checking the audit row first. - Still being scanned (QUARANTINED). A newly-seen version is held up to
FAST_PASS_TIMEOUT_MS(default 3s) then quarantined. Retry shortly. - True positive. It may actually be unsafe. Check the audit row before overriding — don’t reflexively allowlist.
Install hangs or is slow
Section titled “Install hangs or is slow”- First request for a never-seen version waits on the scanner (fast-pass, up to
FAST_PASS_TIMEOUT_MS) before resolving. - Scanner backend down/slow: check
/healthzand operator logs;SCANNER_ENABLED+EMBEDDING_ENDPOINT. - Stale manifest cache:
MANIFEST_CACHE_TTL_MS/STALE_MANIFEST_MAX_AGE_MS.
Behind a load balancer / gateway
Section titled “Behind a load balancer / gateway”- Wrong client IP in audit rows / rate-limit buckets: set
TRUSTED_PROXY_HOPSto the number of trusted hops in front of Upwarden (GKE Gateway =1). Default0= trust no proxies. - Dashboard login fails on staging:
COOKIE_SECUREmarks the session cookieSecure, which browsers silently drop over plainhttp://. Set itfalsefor HTTP-only staging.
Dashboard login
Section titled “Dashboard login”- Magic-link “too many requests”:
STYTCH_LOGIN_SEND_MAXsends perSTYTCH_LOGIN_SEND_WINDOW_SEC(default 10 / 15 min).
Reading the error / finding the audit row
Section titled “Reading the error / finding the audit row”Every install and block writes an audit_log row (apiKeyId, package, version, verdict) scoped to your project — the canonical record for why a request was blocked. See Querying the audit log for the query patterns.