hibiken/asynq · error

unexpected return value from Lua script

Error message

unexpected return value from Lua script: %v

What it means

Returned by RDB delete-all operations (deleteAllCmd) when the Lua script returns something other than an int64. The script should return the number of deleted tasks as an integer; this guard rejects any other reply type. As with the sibling errors, it indicates a Redis reply-protocol deviation.

Solutions

  1. Test the script directly with redis-cli EVAL to inspect the reply type
  2. Ensure the Redis server is unmodified standard Redis
  3. Check the actual value in the error message for type clues
  4. Pin asynq and go-redis versions and retest
Defensive patterns

Strategy: try-catch

Validate before calling

_ = inspector.QueueSize(q) // smoke-test EVAL integer replies before delete-all

Type guard

func toInt64(res interface{}) (int64, bool) {
    switch v := res.(type) {
    case int64: return v, true
    case string:
        n, err := strconv.ParseInt(v, 10, 64)
        return n, err == nil
    }
    return 0, false
}

Try / catch

n, err := inspector.DeleteAllPendingTasks(q)
if err != nil {
    if strings.Contains(err.Error(), "unexpected return value from Lua script") {
        // verify server with redis-cli EVAL before retrying destructive op
    }
    return err
}

Prevention

When it happens

Trigger: Calling DeleteAllPendingTasks (or sibling delete-all methods) where the script's Result() cannot be asserted to int64 — non-standard Redis server, proxy rewriting replies, or patched scripts.

Common situations: Redis-compatible services with differing Lua reply semantics; version drift between asynq's scripts and the server; debugging with a RESP proxy.

Related errors


AI-assisted analysis of hibiken/asynq@d135f1439b (2026-09-07). Data as JSON: /api/errors/632cf0bcc37c497d. Report an issue: GitHub.

Appendix: source

Thrown at internal/rdb/inspect.go:1693

end
redis.call("DEL", KEYS[1])
return table.getn(ids)`)

func (r *RDB) deleteAll(key, qname string) (int64, error) {
	if err := r.checkQueueExists(qname); err != nil {
		return 0, err
	}
	argv := []interface{}{
		base.TaskKeyPrefix(qname),
		qname,
	}
	res, err := deleteAllCmd.Run(context.Background(), r.client, []string{key}, argv...).Result()
	if err != nil {
		return 0, err
	}
	n, ok := res.(int64)
	if !ok {
		return 0, fmt.Errorf("unexpected return value from Lua script: %v", res)
	}
	return n, nil
}

// deleteAllAggregatingCmd deletes all tasks from the given group.
//
// Input:
// KEYS[1] -> asynq:{<qname>}:g:<gname>
// KEYS[2] -> asynq:{<qname>}:groups
// -------
// ARGV[1] -> task key prefix
// ARGV[2] -> group name
var deleteAllAggregatingCmd = redis.NewScript(`
local ids = redis.call("ZRANGE", KEYS[1], 0, -1)
for _, id in ipairs(ids) do
	redis.call("DEL", ARGV[1] .. id)
end
redis.call("SREM", KEYS[2], ARGV[2])

View on GitHub (pinned to d135f1439b)