Skip to content

fix(error): detect READONLY errors from Lua scripts on read-only replicas#3769

Merged
ndyakov merged 1 commit into
redis:masterfrom
zhengjilei:fix/lua-script-readonly-error-detection
Apr 9, 2026
Merged

fix(error): detect READONLY errors from Lua scripts on read-only replicas#3769
ndyakov merged 1 commit into
redis:masterfrom
zhengjilei:fix/lua-script-readonly-error-detection

Conversation

@zhengjilei

@zhengjilei zhengjilei commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Detect READONLY errors embedded in Lua script error messages on read-only replicas
  • Check for the exact substring "-READONLY You can't write against a read only replica" to avoid false positives
  • Add comprehensive unit tests for both direct and wrapped error detection

Supersedes #2770 — incorporates reviewer feedback from @ndyakov (stricter matching) and @ofekshenawa (unit tests).

Problem

When a Lua script containing a write command executes against a read-only replica in a Redis Cluster, the error may contain -READONLY You can't write against a read only replica embedded within a larger error message rather than starting with READONLY :

ERR Error running script (call to f_<sha>): @user_script:<line>: -READONLY You can't write against a read only replica.

The existing code only checks strings.HasPrefix(s, "READONLY "), missing this embedded format. This causes:

  • shouldRetry() to not retry the command
  • isReadOnlyError() to not detect it, preventing connection pool cleanup via isBadConn()
  • LazyReload() to not trigger cluster state refresh

Changes

File Change
internal/proto/redis_errors.go Extended IsReadOnlyError() to check for "-READONLY You can't write against a read only replica" substring
error.go Added same fallback check in shouldRetry()
internal/proto/redis_errors_test.go Added TestIsReadOnlyErrorWithLuaScriptErrors with 7 test cases
error_wrapping_test.go Added TestLuaScriptReadOnlyError with 5 test cases + retry test
error_test.go Added Lua READONLY error to existing shouldRetry ginkgo test

Addressing PR #2770 Reviewer Feedback

@ndyakov's concern: The original PR used strings.Contains(s, "-READONLY") which is too broad — could match keys like "something-READONLY".

This fix: Checks for the exact substring "-READONLY You can't write against a read only replica" — specific enough to only match actual READONLY errors without any false positives.

Integration Test Report (Redis 7.2.7 Cluster)

Tested against a local 6-node Redis Cluster (3 masters + 3 replicas):

======================================================================
REDIS CLUSTER INTEGRATION TEST REPORT
======================================================================
Redis version: 7.2.7
Replica: 127.0.0.1:7104 -> Master: 127.0.0.1:7101 (slots: 5461-10922)
Test key: "testkey-12591" (slot 5462)

Test 1: EVAL with write command on replica (Lua READONLY)
  Error:          "READONLY You can't write against a read only replica.
                   script: d8f2fad9f8e86a53d2a6ebd960b33c4972cacc37,
                   on @user_script:1."
  IsReadOnlyError: true
  ShouldRetry:     true
  Result:          PASS

Test 2: Script.Run with write command on replica
  Error:          "READONLY You can't write against a read only replica.
                   script: d8f2fad9f8e86a53d2a6ebd960b33c4972cacc37,
                   on @user_script:1."
  IsReadOnlyError: true
  ShouldRetry:     true
  Result:          PASS

Test 3: Multi-command Lua script on replica
  Error:          "READONLY You can't write against a read only replica.
                   script: 02c9e874aad609b773e015a3025d0e43fb7b732b,
                   on @user_script:3."
  IsReadOnlyError: true
  ShouldRetry:     true
  Result:          PASS

Test 4: Simulated old-format Lua READONLY
  Error:          "ERR Error running script (call to f_abc123):
                   @user_script:1: -READONLY You can't write against
                   a read only replica."
  IsReadOnlyError: true
  ShouldRetry:     true
  Result:          PASS

======================================================================
ALL TESTS PASSED
======================================================================

Test plan

  • Unit tests for IsReadOnlyError with Lua script error format (proto package)
  • Unit tests for ShouldRetry with Lua script READONLY error
  • Unit tests for wrapped Lua script READONLY error detection
  • False positive tests (non-script errors, script errors without READONLY)
  • Integration test against local Redis 7.2.7 Cluster
  • All existing tests pass (go test ./internal/proto/ and go test -run Test)

🤖 Generated with Claude Code

@jit-ci

jit-ci Bot commented Apr 8, 2026

Copy link
Copy Markdown

Hi, I’m Jit, a friendly security platform designed to help developers build secure applications from day zero with an MVS (Minimal viable security) mindset.

In case there are security findings, they will be communicated to you as a comment inside the PR.

Hope you’ll enjoy using Jit.

Questions? Comments? Want to learn more? Get in touch with us.

@zhengjilei zhengjilei force-pushed the fix/lua-script-readonly-error-detection branch from d2e8a89 to 13241c0 Compare April 8, 2026 12:20

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit 13241c0. Configure here.

Comment thread error.go Outdated
…icas

When a Lua script containing a write command is executed against a
read-only replica in a Redis Cluster, the error may contain
"-READONLY You can't write against a read only replica" embedded
within a larger error message rather than starting with "READONLY ".

The existing code only checked for the "READONLY " prefix, missing
this format. This caused shouldRetry() and isReadOnlyError() to fail,
preventing automatic retry and connection pool cleanup.

The fix checks for the exact substring
"-READONLY You can't write against a read only replica" which is
specific enough to avoid false positives while catching the Lua
script error format.

Closes redis#2770

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@zhengjilei zhengjilei force-pushed the fix/lua-script-readonly-error-detection branch from 13241c0 to 9a2a9e3 Compare April 8, 2026 12:30

@ndyakov ndyakov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks good @zhengjilei, thank you!

@ndyakov ndyakov added the bug label Apr 9, 2026
@ndyakov ndyakov merged commit c4770cd into redis:master Apr 9, 2026
56 of 57 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants