{"id":"3010c8ec51713671","repo":"gofiber/fiber","slug":"failed-to-unmarshal-response-w","errorCode":null,"errorMessage":"failed to unmarshal response: %w","messagePattern":"failed to unmarshal response: %w","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"middleware/idempotency/idempotency.go","lineNumber":76,"sourceCode":"\t// Snapshot the configured names so later mutation of the caller's slice\n\t// cannot change an already-constructed handler. Matching uses\n\t// utils.EqualFold, so no lowercased copies are needed and comparing\n\t// against the canonical-case names fasthttp reports stays allocation-free.\n\tkeepResponseHeaders := slices.Clone(cfg.KeepResponseHeaders)\n\n\tshouldKeepHeader := func(header string) bool {\n\t\treturn slices.ContainsFunc(keepResponseHeaders, func(keep string) bool {\n\t\t\treturn utils.EqualFold(header, keep)\n\t\t})\n\t}\n\n\tmaybeWriteCachedResponse := func(c fiber.Ctx, key string) (bool, error) {\n\t\tif val, err := cfg.Storage.GetWithContext(c, key); err != nil {\n\t\t\treturn false, fmt.Errorf(\"failed to read response: %w\", err)\n\t\t} else if val != nil {\n\t\t\tvar res response\n\t\t\tif _, err := res.UnmarshalMsg(val); err != nil {\n\t\t\t\treturn false, fmt.Errorf(\"failed to unmarshal response: %w\", err)\n\t\t\t}\n\n\t\t\t_ = c.Status(res.StatusCode)\n\n\t\t\tfor header, vals := range res.Headers {\n\t\t\t\tfor _, val := range vals {\n\t\t\t\t\tc.RequestCtx().Response.Header.Add(header, val)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif len(res.Body) != 0 {\n\t\t\t\tif err := c.Send(res.Body); err != nil {\n\t\t\t\t\treturn true, err\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t_ = c.Locals(localsKeyIsFromCache, true)\n","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/gofiber/fiber/blob/9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c/middleware/idempotency/idempotency.go#L58-L94","documentation":"Returned by maybeWriteCachedResponse (idempotency.go:75-76) when msgpack UnmarshalMsg fails on the bytes returned by Storage.Get. The cached payload is corrupted or was written by an incompatible schema version of this middleware.","triggerScenarios":"Storage holds bytes for the key that are not a msgpack-encoded response (manual write, key collision with another subsystem); an upgrade changed the response struct fields; partial writes from a crashed instance; storage backend bit-rot.","commonSituations":"Deploying a new version of the idempotency middleware whose response struct shape differs; two services sharing a Storage namespace and colliding on key prefixes; backup restore of stale data.","solutions":["Namespace idempotency keys so they cannot collide with other Storage users (prefix the key in your KeyGenerator).","After a schema-changing upgrade, flush the affected Storage keys or bump the key prefix.","Investigate any non-idempotency code writing to the same Storage keyspace."],"exampleFix":"// before — keys share namespace with other apps\nKeyHeader: \"X-Idempotency-Key\"\n\n// after — prefix keys so collisions are impossible\nNext: nil,\n// (wrap storage or use a prefixed storage adapter so all keys start with \"idem:\")","handlingStrategy":"validation","validationCode":"// namespace keys to prevent collisions\nKeyGenerator: func(c fiber.Ctx) string {\n    return \"idem:\" + c.Get(\"X-Idempotency-Key\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Prefix all idempotency Storage keys so no other subsystem can collide.","Flush Storage keys after upgrading the middleware across schema changes.","Never let unrelated code write to the same Storage keyspace."],"tags":["idempotency","msgpack","serialization","storage"],"analyzedSha":"9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c","analyzedAt":"2026-08-04T21:44:03.395Z","schemaVersion":2}