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: 'lease is not renewable' and Expired Dynamic Secret Leases

Quick answer

Fix Vault's 'lease is not renewable' error: understand ttl vs max_ttl, non-renewable leases, secrets tune defaults, role-level TTL overrides, and when to re-request credentials.

  • #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 lease renew database/creds/app-readonly/8x2Qk1mZpLtRvN4cB7yWfHsD
Error renewing lease: Error making API request.

URL: PUT https://vault.example.com:8200/v1/sys/leases/renew
Code: 400. Errors:

* lease is not renewable

A closely related failure appears once the lease has already lapsed:

Error renewing lease: Error making API request.

URL: PUT https://vault.example.com:8200/v1/sys/leases/renew
Code: 400. Errors:

* lease not found

What It Means

When a dynamic secrets engine issues a credential, Vault attaches a lease to it: an identifier, a TTL, and a renewable flag. Renewal is not a right — it is a property of the lease, set at issuance from the mount’s tuning and the role’s configuration. When you call sys/leases/renew against a lease whose renewable field is false, Vault rejects the request outright with lease is not renewable. The credential is still perfectly valid until its TTL expires; you simply cannot extend it.

The second message means something different and is often confused with the first. lease not found indicates the lease has already expired (or been revoked) and Vault has purged it from the lease store, revoking the underlying credential — the database user was dropped, the AWS access key was deleted, the certificate was left to lapse. At that point renewal is impossible by definition and the application must request a brand-new secret. Distinguishing “cannot extend” from “already gone” is the first diagnostic step, because the remedies are completely different: one is a TTL policy change, the other is a client retry-and-reauthenticate bug.

Common Causes

  • The secrets engine or role issued the lease with renewable=false, so no renewal is ever permitted.
  • The lease has already reached its max_ttl ceiling; Vault stops extending once total lifetime would exceed it.
  • The application slept past the TTL and only tried to renew after expiry, hitting lease not found.
  • The code is trying to renew a KV v2 static read, which returns no lease at all (lease_id is empty).
  • The role’s max_ttl is far shorter than the mount default, silently capping renewals earlier than expected.
  • A mount was tuned with a low default_lease_ttl and no renewal loop exists on the client side.

Diagnostic Commands

Inspect the lease itself — the renewable and ttl fields answer most questions immediately:

vault lease lookup database/creds/app-readonly/8x2Qk1mZpLtRvN4cB7yWfHsD

The same information via the API path, useful from a script or from inside a container:

vault write sys/leases/lookup \
  lease_id=database/creds/app-readonly/8x2Qk1mZpLtRvN4cB7yWfHsD

Enumerate outstanding leases under a prefix to see what is actually alive (requires a policy with list on sys/leases/lookup/):

vault list sys/leases/lookup/database/creds/app-readonly

Check the mount’s TTL tuning, which sets the ceiling every role inherits:

vault read sys/mounts/database/tune

Read the role to see whether it overrides the mount defaults:

vault read database/roles/app-readonly

Re-run the failing operation with the raw response visible so you can confirm whether a lease was issued at all:

vault read -format=json database/creds/app-readonly | jq '{lease_id, lease_duration, renewable}'

Step-by-Step Resolution

  1. Determine which failure you have. If vault lease lookup succeeds but shows renewable false, this is a configuration issue. If it returns invalid lease ID, the lease is gone and the client must re-request:
vault lease lookup <lease_id> || echo "lease already expired or revoked"
  1. If the lease is genuinely non-renewable by design, change the client. Applications should re-read the credential path rather than looping on renewal:
vault read database/creds/app-readonly
  1. If renewals worked and then stopped, you have hit max_ttl. Compare the lease’s total age against the role and mount ceilings, then raise the ceiling deliberately:
vault secrets tune -default-lease-ttl=1h -max-lease-ttl=24h database/
  1. Set the TTLs at the role level where they belong, so one noisy workload does not force a global change. Role values are capped by the mount’s max_lease_ttl, so raise the mount first if needed:
vault write database/roles/app-readonly \
  db_name=app-postgres \
  creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
  default_ttl=1h \
  max_ttl=24h
  1. Verify the new tuning by issuing a fresh credential and confirming the returned lease_duration and renewable flag:
vault read -format=json database/creds/app-readonly | jq '{lease_id, lease_duration, renewable}'
  1. Stop hand-rolling renewal loops. Let Vault Agent maintain the lease and write the credential to a file the application re-reads, so renewal and re-issuance are both handled outside your code:
template {
  contents    = "{{ with secret \"database/creds/app-readonly\" }}{{ .Data.username }}:{{ .Data.password }}{{ end }}"
  destination = "/run/secrets/db-creds"
  command     = "systemctl reload myapp"
}

Agent renews while it can and transparently re-requests when a lease becomes non-renewable or reaches max_ttl, then runs command so the app picks up the new value. If Agent itself cannot authenticate to fetch the credential in the first place, the symptom is different — see Vault error: “missing client token”.

Prevention

  • Treat every dynamic credential as disposable; write clients that can re-request at any time, not just renew.
  • Set default_ttl/max_ttl on roles rather than relying on mount-wide defaults that affect unrelated workloads.
  • Keep max_ttl comfortably longer than your longest legitimate job, but short enough to bound blast radius.
  • Use Vault Agent or a well-tested SDK lifetime watcher instead of custom renewal timers.
  • Alert on lease count per mount so a leaking client is caught before it exhausts the lease store.
  • Revoke explicitly when work finishes with vault lease revoke instead of letting leases idle to expiry.
  • lease not found — the lease already expired or was revoked; request a new secret rather than renewing.
  • invalid lease ID — malformed or truncated lease identifier, often a copy/paste or shell-quoting problem.
  • no handler for route on a creds path — the secrets engine is not mounted where the client thinks it is.
  • 1 error occurred: * permission denied on sys/leases/renew — the token’s policy lacks update on the renew path.

Frequently Asked Questions

Why does my KV secret have no lease to renew? Static KV reads are not dynamic credentials, so Vault returns an empty lease_id and a lease_duration of zero. There is nothing to renew — just re-read the path when you need the current value. If the read itself fails, see Vault error: “no value found at path”.

Can I renew past max_ttl if I renew often enough? No. max_ttl bounds the total lifetime of the lease from issuance, not the length of a single renewal. Vault will progressively shorten the granted increment as you approach the ceiling and then refuse further renewal. Raise max_ttl on the role or mount if the workload genuinely needs longer.

Does a Vault restart or seal invalidate my leases? No, provided storage is intact — leases live in the storage backend and their expiration timers are rebuilt when Vault unseals. Renewals will fail while the node is sealed or forwarding, which looks like an outage rather than a lease problem; see Vault error: “Vault is sealed”.

Should I revoke leases manually or let them expire? Revoke explicitly when a job finishes: vault lease revoke <lease_id> (or -prefix for a whole path) removes the underlying credential immediately instead of leaving a valid database user around for the remainder of its TTL. For more on lease and TTL design, browse 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.