avoid-throw-in-catch-block

Same as avoid-throw-in-catch-block in Dart Code Metrics

Path
lint/suspicious/avoid-throw-in-catch-block
Default
Off, opt in
Origin
Dart Code Metrics
Rule
145 of 512

Flags throw statements 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. Every throw statement is reported, including throws of existing values; rethrow is always exempt. Delayed local functions and closures are separate execution scopes and are not part of the surrounding catch.

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();
  }
}

Configure avoid-throw-in-catch-block

Set its severity in falcon.json. See the configuration guide.

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

Or silence one occurrence with a suppression comment; the reason is required.

example.dartdart
// falcon-ignore lint/suspicious/avoid-throw-in-catch-block: <reason>