MyCATApache/Mycat-Server · error

ER_USER_READ_ONLY

ER_USER_READ_ONLY

Error message

User readonly

What it means

When a statement falls into the default case of ServerQueryHandler.query (i.e. a write or otherwise non-handled statement type) and the current user is configured as read-only (readOnly flag on the front-end connection), MyCat rejects it with ER_USER_READ_ONLY and message 'User readonly'.

Solutions

  1. Use a non-read-only MyCat user for write operations.
  2. If writes are legitimate, set readOnly=false (or remove readOnly) for that user in server.xml and reconnect.
  3. Check application connection config points at the correct user profile.

Example fix

// before (server.xml)
<user name="app"><property name="readOnly">true</property>...</user>
// after
<user name="app"><property name="readOnly">false</property>...</user>
Defensive patterns

Strategy: validation

Validate before calling

// Check statement type against user's read-only profile before executing
boolean isWrite = matchesAny(sql, "INSERT", "UPDATE", "DELETE", "CREATE", "DROP", "ALTER", "TRUNCATE");
if (isWrite && currentUserReadOnly) {
    throw new SecurityException("user is read-only; refuse write: " + sql);
}

Try / catch

try {
    stmt.execute(sql);
} catch (SQLException e) {
    if (e.getErrorCode() == 1820 /* ER_USER_READ_ONLY */) {
        throw new SecurityException("Switch to a writable MyCat user for this operation", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: A user marked read-only (readOnly=true in server.xml user config) issues INSERT/UPDATE/DELETE/DDL or any statement reaching the default branch.

Common situations: Reporting/analytics user accidentally used for writes; app deployed with wrong credentials; DDL migrations run under a read-only account.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of MyCATApache/Mycat-Server@65f8d8beb7 (2026-09-11). Data as JSON: /api/errors/a21abfb516ae64a2. Report an issue: GitHub.

Appendix: source

Thrown at src/main/java/io/mycat/server/ServerQueryHandler.java:165

		        e.printStackTrace();
                String msg = "Mycat is not connected to zookeeper!!\n";
                msg += "Please start zookeeper and restart mycat so that this mycat can temporarily execute the migration command.If other mycat does not connect to this zookeeper, they will not be able to perceive changes in the migration task.\n";
                msg += "After starting zookeeper,you can command tas follow:\n\nmigrate -table=schema.test -add=dn2,dn3 -force=true\n\nto perform the migration.\n";
                LOGGER.error(e.getMessage());
                LOGGER.error(msg);
                c.writeErrMessage(ErrorCode.ER_UNKNOWN_ERROR, msg);
            }
			break;
		}
		case ServerParse.LOCK:
        	c.lockTable(sql);
        	break;
        case ServerParse.UNLOCK:
        	c.unLockTable(sql);
        	break;
		default:
			if(readOnly){
				LOGGER.warn(new StringBuilder().append("User readonly:").append(sql).toString());
				c.writeErrMessage(ErrorCode.ER_USER_READ_ONLY, "User readonly");
				break;
			}
			c.execute(sql, rs & 0xff);
		}

		switch (sqlType) {
			case ServerParse.SELECT:
			case ServerParse.DELETE:
			case ServerParse.UPDATE:
			case ServerParse.INSERT:
			case ServerParse.COMMAND:
				// curd 在后面会更新
				break;
			default:
				c.setExecuteSql(null);
		}
	}

View on GitHub (pinned to 65f8d8beb7)