Rules / Suspicious / avoid-throw-in-catch-block

avoid-throw-in-catch-block

lint/suspicious/avoid-throw-in-catch-block
recommended
Same as avoid-throw-in-catch-block · Dart Code Metrics

Flags a throw of a new object inside a catch block.

Throwing a fresh exception from a catch replaces the error in flight along with its original stack trace, so the root cause is lost and the failure becomes much harder to diagnose. Prefer rethrow to propagate the caught exception unchanged, or wrap it in an error that keeps the original as a cause. Only a throw statement raising a new value is reported; a rethrow is always exempt.

Invalid

example.dartdart
// Test cases for avoid-throw-in-catch-block rule
// All violations are annotated inline

void readFile() {
  try {
    final file = File('missing.txt').readAsStringSync();
  } catch (e) {
    throw Exception('Failed to read file: $e');
  }
}

Future<String> fetchData() {
  try {
    return http.get(url);
  } catch (e, st) {
    throw Exception('Network error: $e');
  }
}

class Database {
  void connect() {
    try {
      _establishConnection();
    } catch (error) {
      throw DatabaseException('Connection failed');
    }
  }

  void update(String query) {
    try {
      _executeQuery(query);
    } on TimeoutException catch (e) {
      throw TimeoutException('Update timeout: ${e.message}');
    } catch (e) {
      throw RuntimeException(e.toString());
    }
  }
}

void parseJson(String json) {
  try {
    jsonDecode(json);
  } catch (e) {
    throw FormatException('Invalid JSON');
  }
}

void nestedTryCatch() {
  try {
    try {
      doSomething();
    } catch (e) {
      throw CustomException('Inner error');
    }
  } catch (e) {
    print('Outer handler: $e');
  }
}

Future<void> multipleThrows() {
  try {
    return riskyOperation();
  } on NotFoundException catch (e) {
    throw ApiException('Not found: ${e.message}');
  } on TimeoutException catch (e) {
    throw ApiException('Timeout: ${e.message}');
  }
}
Avoid throwing exceptions within catch blocks
7 } catch (e) {
8 throw Exception('Failed to read file: $e');
Avoid throwing exceptions within catch blocks
15 } catch (e, st) {
16 throw Exception('Network error: $e');
Avoid throwing exceptions within catch blocks
24 } catch (error) {
25 throw DatabaseException('Connection failed');
Avoid throwing exceptions within catch blocks
32 } on TimeoutException catch (e) {
33 throw TimeoutException('Update timeout: ${e.message}');
Avoid throwing exceptions within catch blocks
34 } catch (e) {
35 throw RuntimeException(e.toString());
Avoid throwing exceptions within catch blocks
43 } catch (e) {
44 throw FormatException('Invalid JSON');
Avoid throwing exceptions within catch blocks
52 } catch (e) {
53 throw CustomException('Inner error');
Avoid throwing exceptions within catch blocks
63 } on NotFoundException catch (e) {
64 throw ApiException('Not found: ${e.message}');
Avoid throwing exceptions within catch blocks
65 } on TimeoutException catch (e) {
66 throw ApiException('Timeout: ${e.message}');

Valid

example.dartdart
// Good cases for avoid-throw-in-catch-block rule
// No violations expected

void readFile() {
  try {
    final file = File('missing.txt').readAsStringSync();
  } catch (e) {
    rethrow;
  }
}

Future<String> fetchData() {
  try {
    return http.get(url);
  } catch (e, st) {
    rethrow;
  }
}

class Database {
  void connect() {
    try {
      _establishConnection();
    } catch (error) {
      rethrow;
    }
  }

  void update(String query) {
    try {
      _executeQuery(query);
    } on TimeoutException catch (e) {
      rethrow;
    } catch (e) {
      rethrow;
    }
  }
}

void parseJson(String json) {
  try {
    jsonDecode(json);
  } catch (e) {
    print('Failed to parse JSON: $e');
  }
}

void handleGracefully() {
  try {
    riskyOperation();
  } catch (e) {
    logger.error('Operation failed', error: e);
  }
}

void logAndReturn() {
  try {
    doSomething();
  } catch (e) {
    _handleError(e);
  }
}

Future<String> withFallback() {
  try {
    return fetchData();
  } catch (e) {
    return Future.value('default');
  }
}

void withCallback() {
  try {
    process();
  } catch (e) {
    onError?.call(e);
  }
}

class ErrorHandler {
  void handle(Error error) {
    try {
      _sendToServer(error);
    } catch (e) {
      logger.warn('Could not send error to server: $e');
    }
  }
}

void cleanupOnError() {
  try {
    resource.allocate();
    resource.use();
  } catch (e) {
    resource.dispose();
  }
}

How to configure

Set the severity of avoid-throw-in-catch-block in your falcon.json:

falcon.jsonjson
{
  "linter": {
    "rules": {
      "suspicious": {
        "avoid-throw-in-catch-block": "error"
      }
    }
  }
}