{"record":{"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":500,"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/a105acad6c1e4576a77f01e02973f67e962bb58d/middleware/cache/manager.go#L105-L141","documentation":"Returned when bytes fetched from external Storage cannot be decoded back into an item via msgp UnmarshalMsg. The data exists in storage but is not a valid msgpack-encoded cache item, signaling either corruption, a schema/version mismatch, or foreign data written under the same key namespace.","triggerScenarios":"Cache lookup hits a key whose stored bytes fail it.UnmarshalMsg: another process/version wrote a different msgp schema under the same key prefix; storage corruption (truncated write, partial flush); key-namespace collision with another Fiber app sharing the same Redis DB without key isolation; msgp-generated code regenerated with different field limits after entries were written.","commonSituations":"Upgrading Fiber/cache versions where the item struct changed shape; two apps pointed at the same Redis DB with overlapping cache keys; storage engine crash mid-write leaving partial bytes; using a key prefix that collides with non-cache data.","solutions":["Use a unique key prefix/namespace per app and per Fiber major version so cached bytes are never shared across incompatible schemas.","Flush the affected Storage namespace after upgrading the cache middleware (the version mismatch self-heals as old entries expire/are overwritten).","Verify no other writer is putting arbitrary bytes under keys your cache middleware owns.","Confirm the msgp-generated code (manager_msgp.go) matches the item struct in the running binary — rebuild after schema changes."],"exampleFix":"// before: unmarshal failure surfaces as a hard error\nif _, err := it.UnmarshalMsg(raw); err != nil {\n    m.release(it)\n    return nil, fmt.Errorf(\"cache: failed to unmarshal key %q: %w\", m.logKey(key), err)\n}\n\n// after: treat as corrupt entry — delete it and miss\nif _, err := it.UnmarshalMsg(raw); err != nil {\n    m.release(it)\n    _ = m.storage.DeleteWithContext(ctx, key) // best-effort purge of stale/corrupt entry\n    return nil, errCacheMiss\n}","handlingStrategy":"type-guard","validationCode":"// Use an isolated key namespace per app + Fiber major version so stale,\n// differently-encoded entries can never be read by this code path.\nconst cacheKeyPrefix = \"appA:fiberV3:\"\n\nfunc cacheKey(parts ...string) string {\n    return cacheKeyPrefix + strings.Join(parts, \":\")\n}","typeGuard":"// Guard the decoded value is a plausible msgp item before returning it.\nfunc looksLikeItem(raw []byte) bool {\n    // msgp array prefix for our item: a msgpack array header followed by expected shape.\n    // Cheap structural check; full validation happens in UnmarshalMsg.\n    return len(raw) > 0 && raw[0] == 0x9f || (raw[0]&0xf0 == 0x90) // fixarray family\n}","tryCatchPattern":"if _, err := it.UnmarshalMsg(raw); err != nil {\n    m.release(it)\n    // corrupt/incompatible entry — purge and miss rather than error\n    _ = m.storage.DeleteWithContext(ctx, key)\n    log.Warn(\"purged unreadable cache entry:\", key, err)\n    return nil, errCacheMiss\n}","preventionTips":["Namespace cache keys per app and per Fiber version.","Flush the cache namespace after upgrades that change the item schema.","Regenerate manager_msgp.go (make generate) whenever the item struct changes."],"tags":["cache","serialization","msgp","storage","version-skew","go","fiber"],"backgroundTag":null,"analyzedSha":"a105acad6c1e4576a77f01e02973f67e962bb58d","analyzedAt":"2026-08-11T17:33:26.942Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}