Skip to content
🎉 Launch sale:50% off everything over $22 — automatically applied at checkout· ends Aug 2Shop the sale →
DevOps AI ToolKit
Newsletter
All guides
AI for HashiCorp Vault By James Joyner IV · · 9 min read Last reviewed Jul 2026

Vault Error: 'missing client token' on Every API Call

Quick answer

Fix Vault's 400 'missing client token': set VAULT_TOKEN, send X-Vault-Token, use vault login and ~/.vault-token, and wire CI runners and Vault Agent sinks correctly.

  • #vault
  • #secrets
  • #security-hardening
  • #troubleshooting
  • #errors
Free toolkit

Stuck on this HashiCorp Vault error? Get the free incident triage checklist

A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.

Exact Error Message

$ vault kv get secret/app/config
Error making API request.

URL: GET https://vault.example.com/v1/secret/data/app/config
Code: 400. Errors:

* missing client token

The same condition from a raw HTTP client that forgot the header:

$ curl -s -i https://vault.example.com/v1/auth/token/lookup-self
HTTP/2 400
content-type: application/json

{"errors":["missing client token"]}

What It Means

Vault could not find a token on the request at all. Every path outside the small unauthenticated set — sys/health, sys/seal-status, sys/init, and the login endpoints under auth/*/login — requires a client token, supplied either in the X-Vault-Token header or as Authorization: Bearer <token>. When neither header is present, Vault rejects the request before any policy evaluation happens and returns HTTP 400.

The status code is the key distinction. A 400 with missing client token means no credential was sent. A 403 with permission denied means a token was sent and Vault either could not validate it or the attached policies did not allow the operation. If you are debugging a client and see 400, stop looking at policies entirely — the problem is upstream, in how the token is being sourced or passed. For the CLI this almost always means VAULT_TOKEN is unset and there is no ~/.vault-token helper file to fall back on.

Common Causes

  • VAULT_TOKEN is not exported in the shell or process environment.
  • A custom HTTP client sends no X-Vault-Token header, or spells it wrong.
  • ~/.vault-token does not exist because vault login was never run, or runs as a different user.
  • A CI/CD runner starts with a clean environment and no login step before the Vault call.
  • A Vault Agent sink file is empty or not yet written when the application reads it.
  • The token is passed as a shell variable that was never exported, so child processes do not inherit it.

Diagnostic Commands

Confirm whether the CLI has a token at all, and where it came from:

vault print token
env | grep -E '^VAULT_(TOKEN|ADDR|NAMESPACE)='

Check for the token helper file the CLI writes on login:

ls -l ~/.vault-token && wc -c ~/.vault-token

Verify the token is valid by looking it up against the server:

vault token lookup -format=json | jq '.data | {display_name, policies, ttl}'

Reproduce with an explicit header so you can prove the transport is fine:

curl -s -H "X-Vault-Token: $VAULT_TOKEN" \
  https://vault.example.com/v1/auth/token/lookup-self | jq

For agent-based setups, confirm the sink file exists and is non-empty:

ls -l /run/vault/agent-token && head -c 8 /run/vault/agent-token

Step-by-Step Resolution

  1. Authenticate. vault login writes the resulting token to ~/.vault-token, which the CLI reads automatically on every later command:
vault login -method=userpass username=alice
# or with a plain token
vault login s.XXXXXXXXXXXXXXXX
vault token lookup
  1. For non-interactive use, capture only the token field and export it. -field=token avoids writing the helper file when combined with -no-store:
export VAULT_ADDR=https://vault.example.com
export VAULT_TOKEN="$(vault write -field=token -no-store auth/approle/login \
  role_id="$ROLE_ID" secret_id="$SECRET_ID")"
vault token lookup
  1. Fix HTTP clients by sending the header explicitly. Both forms are accepted; use one, not a malformed mix:
curl -H "X-Vault-Token: $VAULT_TOKEN" \
  https://vault.example.com/v1/secret/data/app/config

curl -H "Authorization: Bearer $VAULT_TOKEN" \
  https://vault.example.com/v1/secret/data/app/config
  1. In CI, log in inside the job rather than assuming an inherited environment. A GitHub Actions or GitLab runner starts clean, and export in an earlier step does not survive into the next one unless you persist it:
# inside a single CI step
export VAULT_ADDR="$VAULT_ADDR"
VAULT_TOKEN="$(vault write -field=token -no-store auth/jwt/login \
  role=ci-deploy jwt="$CI_JOB_JWT")"
export VAULT_TOKEN
vault kv get -field=password secret/data/ci/db

If the AppRole credentials themselves are rejected at this step, that is a different failure — see Vault error: “invalid role_id / secret_id”. For Kubernetes workloads authenticating with a projected service account token, see Vault error: Kubernetes auth service account token.

  1. For Vault Agent, make the application wait for the sink file and read it at request time rather than at process start. Agent writes the sink after its own login succeeds, which may be seconds after your container starts:
auto_auth {
  method "approle" {
    config = {
      role_id_file_path   = "/etc/vault/role_id"
      secret_id_file_path = "/etc/vault/secret_id"
    }
  }

  sink "file" {
    config = {
      path = "/run/vault/agent-token"
      mode = 0640
    }
  }
}
until [ -s /run/vault/agent-token ]; do sleep 1; done
export VAULT_TOKEN="$(cat /run/vault/agent-token)"
  1. If you are on Vault Enterprise with namespaces, set the namespace separately. A namespace is not part of the token header, and putting it in the wrong place produces confusing failures — an unset namespace sends a valid token to the root namespace where the mount does not exist:
export VAULT_NAMESPACE=team-platform
# equivalent header form
curl -H "X-Vault-Token: $VAULT_TOKEN" \
     -H "X-Vault-Namespace: team-platform" \
     https://vault.example.com/v1/secret/data/app/config

Once a token is flowing and you start seeing 403s instead of 400s, you have moved from an authentication problem to an authorization one — continue with Vault error: “permission denied” on a path.

Prevention

  • Standardise on a login step at the start of every job rather than long-lived exported tokens.
  • Use Vault Agent or a sidecar for long-running workloads so token renewal is handled for you.
  • Fail fast in application startup with an explicit check that VAULT_TOKEN (or the sink file) is non-empty.
  • Never bake tokens into images or commit ~/.vault-token; add it to your global gitignore.
  • Set VAULT_ADDR and VAULT_NAMESPACE in the same place you set the token so the three cannot drift apart.
  • Add a health probe that calls auth/token/lookup-self so a missing token surfaces before real traffic does.
  • permission denied (403) — a token was sent, but it is invalid, expired, or lacks the capability.
  • missing client token on sys/ paths — the endpoint is root-protected; unauthenticated access is not available there.
  • namespace not foundX-Vault-Namespace names a namespace that does not exist on the cluster.
  • no handler for route — the mount path is wrong, often the result of an unset namespace on Enterprise.

Frequently Asked Questions

Why does the CLI work on my laptop but fail in CI? Locally the CLI silently reads ~/.vault-token written by an earlier vault login. CI runners have no such file and no exported VAULT_TOKEN, so the request goes out with no header.

Is X-Vault-Token or Authorization: Bearer preferred? Both work. X-Vault-Token is the native header; the Bearer form exists so standard HTTP tooling and proxies can carry the credential. Send exactly one.

Why do I get 400 instead of 403 for a revoked token? You should get 403 for a revoked token. A 400 means the header was absent entirely — check for an empty variable expanding to nothing, which is the usual culprit.

Should I use -no-store when logging in? Yes for automation, so the token is never written to disk and lives only in the process environment. Keep the default file behaviour for interactive human use. More auth-method walkthroughs are in the Vault guides.

Free download · 368-page PDF

Fixed it? Get 500 HashiCorp Vault & DevOps AI prompts — free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.

Did this fix your issue?

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.