{"id":"d744646cdf521b68","repo":"gofiber/fiber","slug":"cache-failed-to-unmarshal-key-q-w","errorCode":null,"errorMessage":"cache: failed to unmarshal key %q: %w","messagePattern":"cache: failed to unmarshal key %q: %w","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"middleware/cache/manager.go","lineNumber":123,"sourceCode":"\te.heapidx = 0\n\tm.pool.Put(e)\n}\n\n// get data from storage or memory\nfunc (m *manager) get(ctx context.Context, key string) (*item, error) {\n\tif m.storage != nil {\n\t\traw, err := m.storage.GetWithContext(ctx, key)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"cache: failed to get key %q from storage: %w\", m.logKey(key), err)\n\t\t}\n\t\tif raw == nil {\n\t\t\treturn nil, errCacheMiss\n\t\t}\n\n\t\tit := m.acquire()\n\t\tif _, err := it.UnmarshalMsg(raw); err != nil {\n\t\t\tm.release(it)\n\t\t\treturn nil, fmt.Errorf(\"cache: failed to unmarshal key %q: %w\", m.logKey(key), err)\n\t\t}\n\n\t\treturn it, nil\n\t}\n\n\tif value := m.memory.Get(key); value != nil {\n\t\tit, ok := value.(*item)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"cache: unexpected entry type %T for key %q\", value, m.logKey(key))\n\t\t}\n\t\treturn it, nil\n\t}\n\n\treturn nil, errCacheMiss\n}\n\n// get raw data from storage or memory\nfunc (m *manager) getRaw(ctx context.Context, key string) ([]byte, error) {","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/gofiber/fiber/blob/9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c/middleware/cache/manager.go#L105-L141","documentation":"Thrown at middleware/cache/manager.go:123 when storage.GetWithContext returned bytes but msgp's it.UnmarshalMsg(raw) failed to decode them back into the internal *item struct. The bytes are present but not a valid msgpack payload matching the cache's item schema. This is a data-integrity / version-drift signal, not a transient network error.","triggerScenarios":"Deploying a new version of fiber/cache whose msgp-generated item layout differs from the entries already sitting in the shared storage (e.g. fields added/removed, limit tags changed); another service writing arbitrary bytes under a key prefix the cache middleware also reads; storage corruption (truncated value, partial write); manual tampering via redis-cli SET.","commonSituations":"Rolling deploy where old-format cache entries survive in Redis and are read by new-format code; two apps sharing one Redis keyspace with overlapping key prefixes; a migration that changed the item struct without flushing the cache; storage backend returning truncated reads under disk pressure.","solutions":["Flush or namespace-isolate the cache storage after upgrading fiber/cache versions so old msgp payloads cannot be read by new code (e.g. bump CacheModifier / key prefix, or run Reset on the storage).","Confirm no other writer is producing keys under the same prefix used by the cache middleware; give the cache its own exclusive key namespace.","If the wrapped msgp error mentions a specific field/limit, compare the item struct in middleware/cache/manager.go against the version that wrote the data.","Regenerate msgp code with `make generate` after any change to the item struct so encoder/decoder stay in lockstep.","Treat the corrupted entry as stale: delete the offending key and let the cache repopulate on the next request."],"exampleFix":"// before: cache shares the default key namespace with other apps\napp.Use(cache.New(cache.Config{ Storage: redis.New() }))\n\n// after: versioned key prefix isolates schema generations; flush old on deploy\napp.Use(cache.New(cache.Config{\n    Storage:      redis.New(),\n    CacheModifier: func(c fiber.Ctx) string {\n        return \"v2:\" + c.Path() // bump to v3 on next schema change\n    },\n}))","handlingStrategy":"validation","validationCode":"// At deploy, ensure the cache keyspace is isolated per schema generation.\nstore.Reset() // or bump the key prefix in CacheModifier\n// Then validate a round-trip works:\nitem := newItemSample()\nraw, _ := item.MarshalMsg(nil)\n_ = store.SetWithContext(ctx, \"probe:v2\", raw, time.Minute)\nback, err := store.GetWithContext(ctx, \"probe:v2\")\nvar it2 item\nif _, err := it2.UnmarshalMsg(back); err != nil {\n    log.Fatalf(\"cache schema mismatch: %v\", err)\n}","typeGuard":null,"tryCatchPattern":"// Distinguish a corrupt/unmarshalable entry from a real failure.\nraw, err := m.storage.GetWithContext(ctx, key)\nif err == nil && raw != nil {\n    var it item\n    if _, uerr := it.UnmarshalMsg(raw); uerr != nil {\n        log.Printf(\"deleting corrupt cache entry %q: %v\", key, uerr)\n        _ = m.storage.DeleteWithContext(ctx, key) // reclaim; let next request repopulate\n        return nil, errCacheMiss\n    }\n}","preventionTips":["Flush or re-namespace the cache (bump CacheModifier prefix) whenever the item struct changes.","Give the cache its own exclusive key prefix in shared storage.","Regenerate msgp with `make generate` after any item-struct change.","Never manually SET keys under the cache namespace from other services."],"tags":["cache","serialization","msgpack","version-drift","fiber"],"analyzedSha":"9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c","analyzedAt":"2026-08-04T21:44:03.395Z","schemaVersion":2}