gofiber/fiber · error

limiter: failed to unmarshal key

Error message

limiter: failed to unmarshal key %q: %w

What it means

After a successful storage.GetWithContext, the raw bytes are handed to item.UnmarshalMsg (msgp-generated). If the bytes are not a valid msgpack-encoded item, this error fires. The acquired *item is released back to the pool before returning, so the leak is bounded to the bytes themselves.

Solutions

  1. Flush the affected limiter keys (or the whole storage namespace) so stale, differently-encoded values are cleared: redis-cli KEYS 'limiter:*' | xargs redis-cli DEL.
  2. Re-run go generate for the limiter package (msgp) and confirm manager_msgp.go is committed and matches the current item struct.
  3. Use a distinct key prefix per app/build so a new schema version cannot read an old version's values.
  4. If the corruption is from a shared namespace, set a unique StorageKeyPrefix in limiter.Config.
  5. Roll back to the previous build or migrate storage atomically with the code change.

Example fix

// before: shared prefix causes collision
limiter.New(limiter.Config{Storage: redisStore})

// after: namespace per build, flush on deploy
limiter.New(limiter.Config{Storage: redisStore, StorageKeyPrefix: "v2:limiter:"})
Defensive patterns

Strategy: validation

Validate before calling

// On deploy, verify no stale values collide with the new schema.
// (infra step) redis-cli --scan --pattern 'limiter:*' | head; flush if schema changed.

Try / catch

// Treat unmarshal failures as a poisoned key: delete and continue.
if errors.Is(err, msgp.ErrShortBytes) { // or match the wrapped prefix
  _ = storage.Del(key)
}

Prevention

When it happens

Trigger: Stored value for the limiter key was written by a different schema version (struct field reordered/renamed after msgp regeneration without clearing storage), the key namespace is shared with another fiber.Storage consumer that wrote arbitrary bytes, manual storage inspection/repopulation with wrong format, or storage corruption (truncated Redis value after a crash).

Common situations: Deploying a new fiber version where the internal `item` struct's msgp shape changed without flushing Redis; co-locating limiter keys in the same Redis DB as session keys with a colliding prefix; a Redis RDB/AOF restore from a backup made by a different app build.

Related errors


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

Appendix: source

Thrown at middleware/limiter/manager.go:76

func (m *manager) release(e *item) {
	e.prevHits = 0
	e.currHits = 0
	e.exp = 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("limiter: failed to get key %q from storage: %w", m.logKey(key), err)
		}
		if raw != nil {
			it := m.acquire()
			if _, err := it.UnmarshalMsg(raw); err != nil {
				m.release(it)
				return nil, fmt.Errorf("limiter: failed to unmarshal key %q: %w", m.logKey(key), err)
			}
			return it, nil
		}
		return m.acquire(), nil
	}

	value := m.memory.Get(key)
	if value == nil {
		return m.acquire(), nil
	}

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

	return it, nil
}

View on GitHub (pinned to a105acad6c)