gofiber/fiber · error

cache: failed to unmarshal key

Error message

cache: failed to unmarshal key %q: %w

What it means

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.

Solutions

  1. Use a unique key prefix/namespace per app and per Fiber major version so cached bytes are never shared across incompatible schemas.
  2. Flush the affected Storage namespace after upgrading the cache middleware (the version mismatch self-heals as old entries expire/are overwritten).
  3. Verify no other writer is putting arbitrary bytes under keys your cache middleware owns.
  4. Confirm the msgp-generated code (manager_msgp.go) matches the item struct in the running binary — rebuild after schema changes.

Example fix

// before: unmarshal failure surfaces as a hard error
if _, err := it.UnmarshalMsg(raw); err != nil {
    m.release(it)
    return nil, fmt.Errorf("cache: failed to unmarshal key %q: %w", m.logKey(key), err)
}

// after: treat as corrupt entry — delete it and miss
if _, err := it.UnmarshalMsg(raw); err != nil {
    m.release(it)
    _ = m.storage.DeleteWithContext(ctx, key) // best-effort purge of stale/corrupt entry
    return nil, errCacheMiss
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Use an isolated key namespace per app + Fiber major version so stale,
// differently-encoded entries can never be read by this code path.
const cacheKeyPrefix = "appA:fiberV3:"

func cacheKey(parts ...string) string {
    return cacheKeyPrefix + strings.Join(parts, ":")
}

Type guard

// Guard the decoded value is a plausible msgp item before returning it.
func looksLikeItem(raw []byte) bool {
    // msgp array prefix for our item: a msgpack array header followed by expected shape.
    // Cheap structural check; full validation happens in UnmarshalMsg.
    return len(raw) > 0 && raw[0] == 0x9f || (raw[0]&0xf0 == 0x90) // fixarray family
}

Try / catch

if _, err := it.UnmarshalMsg(raw); err != nil {
    m.release(it)
    // corrupt/incompatible entry — purge and miss rather than error
    _ = m.storage.DeleteWithContext(ctx, key)
    log.Warn("purged unreadable cache entry:", key, err)
    return nil, errCacheMiss
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of gofiber/fiber@a105acad6c (2026-08-11). Data as JSON: /api/errors/d744646cdf521b68. Report an issue: GitHub.

Appendix: source

Thrown at middleware/cache/manager.go:123

	e.heapidx = 0
	m.pool.Put(e)
}

// get data from storage or memory
func (m *manager) get(ctx context.Context, key string) (*item, error) {
	if m.storage != nil {
		raw, err := m.storage.GetWithContext(ctx, key)
		if err != nil {
			return nil, fmt.Errorf("cache: failed to get key %q from storage: %w", m.logKey(key), err)
		}
		if raw == nil {
			return nil, errCacheMiss
		}

		it := m.acquire()
		if _, err := it.UnmarshalMsg(raw); err != nil {
			m.release(it)
			return nil, fmt.Errorf("cache: failed to unmarshal key %q: %w", m.logKey(key), err)
		}

		return it, nil
	}

	if value := m.memory.Get(key); value != nil {
		it, ok := value.(*item)
		if !ok {
			return nil, fmt.Errorf("cache: unexpected entry type %T for key %q", value, m.logKey(key))
		}
		return it, nil
	}

	return nil, errCacheMiss
}

// get raw data from storage or memory
func (m *manager) getRaw(ctx context.Context, key string) ([]byte, error) {

View on GitHub (pinned to a105acad6c)