Skip to content

[Bug]: Rslint v0.2.0 Bug - Auto Fixer Incorrectly Inserts Async Keywords #451

@samvallad33

Description

@samvallad33

System Info

System: macOS 26.2, CPU (10) ar64 Apple M4, and Shell 5.9 /bin/zsh
Binaries: Node.js 24.1.0, npm 11.6.2, pnpm 10.28.0, Watchman 2025:08:11:00
Browsers: Installed web browsers. Chrome 144.0.7559.110. Safari: 26.2
npmPackages: React 19.2.4, typescript 5.9.3 -> 5.9.3

Details

I tried this code:

class PanelErrorBoundary extends Component<PanelErrorBoundaryProps, PanelErrorBoundaryState> {
  // ... constructor and other methods ...

  render() {
    if (this.state.hasError) {
      return <PanelError panelId={this.props.panelId} error={this.state.error} onRetry={this.handleReset} />;
    }
    return this.props.children;
  }
}

// And also this arrow function:
const renderPanelContent = () => {
  switch (props.panelId) {
    case PANEL_IDS.CHAT:
      return children;
    // ...
  }
};

I expected this to happen: The code should remain unchanged, or at most receive formatting fixes. The @typescript-eslint/require-await rule is explicitly disabled in our config:

{
  "rules": {
    "@typescript-eslint/require-await": "off"
  }
}

Instead this happened: The auto-fixer inserted multiple async keywords, creating invalid syntax:


-      render() {
+       async  async  async render() {

-    const renderPanelContent =   () => {
+    const renderPanelContent =    async  async  async () => {

This breaks React completely because:

React class component render() must be synchronous to return JSX immediately
Making it async returns a Promise, which React cannot handle
The triple async is invalid JavaScript syntax
Meta

rslint -V: 0.2.0

Additional context

The behavior suggests the fixer is malfunctioning in two ways:

It runs despite @typescript-eslint/require-await being explicitly disabled
It appears to be operating in reverse logic - instead of removing unnecessary async markers, it's adding them recursively in a single pass
This creates a loop where the linter introduces syntax errors rather than resolving them.

Workaround: We've added a warning comment to affected files and have to manually revert the changes after CI runs:

/**

  • ⚠️ LINTER BUG WARNING: The auto-fix workflow (pnpm lint:fix) has a bug that
  • inserts "async async async" before render() and renderPanelContent().
  • If you see "async async async render()" after CI runs, remove the async keywords.
  • React class component render() must be synchronous.
    */

Reproduce link

No response

Reproduce Steps

markdown

Minimal Reproduction

Here are the simplest steps to reproduce the recursive async auto-fix bug:

1. Create a Reproduction File (repro.tsx)

Create a file with the following class component and arrow function pattern. Note that the render() method is synchronous.

import React, { Component } from 'react';

class PanelErrorBoundary extends Component {
  state = { hasError: false, error: null };

  // The fixer targets this synchronous render method
  render() {
    if (this.state.hasError) {
      return <div>Error</div>;
    }
    return this.props.children;
  }
}

// It also targets this arrow function
const renderPanelContent = () => {
  switch (props.panelId) {
    case 'CHAT':
      return children;
  }
};

2. Configuration

Ensure the rule is explicitly disabled in your config:

{
  "rules": {
    "@typescript-eslint/require-await": "off"
  }
}

3. Execute Linter

Run the fix command:

pnpm lint:fix

Actual Result: The linter inserts async async async recursively before render() and the arrow function, causing syntax errors.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions