nektos/act · warning
cache %d: not reserved
Error message
cache %d: not reserved
What it means
The cache upload endpoint (PATCH /caches/:id) first loads the Cache record from the bolthold DB by the numeric ID from the URL. IDs are handed out by the reserve endpoint; if db.Get returns bolthold.ErrNotFound, no live reservation exists for that ID — it was never reserved, was for a different server instance (DB wiped), or the ID is simply wrong — and the server responds HTTP 400 'cache %d: not reserved'.
Source
Thrown at pkg/artifactcache/handler.go:265
// PATCH /_apis/artifactcache/caches/:id
func (h *Handler) upload(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
id, err := strconv.ParseUint(params.ByName("id"), 10, 64)
if err != nil {
h.responseJSON(w, r, 400, err)
return
}
cache := &Cache{}
db, err := h.openDB()
if err != nil {
h.responseJSON(w, r, 500, err)
return
}
defer db.Close()
if err := db.Get(id, cache); err != nil {
if errors.Is(err, bolthold.ErrNotFound) {
h.responseJSON(w, r, 400, fmt.Errorf("cache %d: not reserved", id))
return
}
h.responseJSON(w, r, 500, err)
return
}
if cache.Complete {
h.responseJSON(w, r, 400, fmt.Errorf("cache %v %q: already complete", cache.ID, cache.Key))
return
}
db.Close()
start, _, err := parseContentRange(r.Header.Get("Content-Range"))
if err != nil {
h.responseJSON(w, r, 400, err)
return
}
if err := h.storage.Write(cache.ID, start, r.Body); err != nil {
h.responseJSON(w, r, 500, err)View on GitHub (pinned to 4f41128141)
Solutions
- Re-run the whole workflow so actions/cache performs a fresh reserve -> upload -> commit sequence against the current server.
- Avoid restarting act (or wiping its cache dir) between a job's main and post steps; keep --artifact-server-port stable across runs.
- Ensure only one act artifact-cache server owns the port/DB being targeted.
- If calling the API manually, always POST /caches to reserve first and use the returned ID.
Example fix
# before
# manual API use skipping reserve:
curl -X PATCH .../caches/42 -H 'Content-Range: bytes 0-99' --data-binary @a.tar
# -> 400 cache 42: not reserved
# after
curl -X POST .../caches -d '{"key":"linux-a","version":"1"}' # returns {"cacheID":43,...}
curl -X PATCH .../caches/43 -H 'Content-Range: bytes 0-99' --data-binary @a.tar Defensive patterns
Strategy: validation
Validate before calling
// Reserve before upload when driving the cache API
curl -sX POST "$URL/api/v1/caches" \
-H "Authorization: Bearer $TOKEN" \
-d '{"key":"linux-a","version":"1","cacheSize":1024}'
# use the returned cacheID for PATCH; never reuse IDs from previous server runs Try / catch
resp, err := client.Patch(cacheURL+"/caches/"+id, body)
if err == nil && resp.StatusCode == 400 {
if strings.Contains(readBody(resp), "not reserved") {
// reservation lost (server restarted): start a new reserve->upload->commit cycle
id = reserveFresh(key, version)
resp, err = client.Patch(cacheURL+"/caches/"+id, body)
}
} Prevention
- Always reserve (POST /caches) and use the returned ID in the same server session.
- Keep act's artifact-server port and cache directory stable for the whole workflow.
- Do not restart act between a job's main and post steps when using actions/cache.
When it happens
Trigger: A GitHub Actions cache client PATCHing to a cache ID that was not reserved in this server's database: act restarted between reserve and upload (fresh temp DB loses reservations), multiple act instances behind one address, replayed/stale requests after cache eviction, or hand-crafted API calls skipping the reserve step.
Common situations: actions/cache's post step failing after act was re-run mid-workflow; a long workflow spanning an act restart; CI scripts caching the wrong port after changing --artifact-server-port; parallel jobs interleaving IDs from different servers.
Related errors
- cache %v %q: already complete
- unable to determine outbound IP address
- generate auth token: %w
- find cache: %w
- insert cache: %w
AI-assisted analysis of nektos/act@4f41128141 (2026-08-15).
Data as JSON: /api/errors/98883b55111559a3.
Report an issue: GitHub.