@catch handler placed after a catch-all @catch (id)

ID

objectivec.correctness.catch_handler_after_ellipsis

Severity

low

Remediation Complexity

medium

Remediation Risk

low

Remediation Effort

low

Resource

Error Handling

Language

Objective-C

Description

Reports an @catch clause written after @catch (id …​) in the same @try. Only id is treated as the catch-all — a first handler typed NSException * is not, because an object of any class can be thrown. Handlers in a later @try, in a later method, or after @finally are not reported.

Rationale

@catch (id) accepts every thrown object, so no exception ever reaches a handler below it: the clause is dead code that reads as if it were live. Whoever wrote the specific handler expected it to run, and whoever reads the method later will believe that class of failure is handled — the recovery, the logging and the cleanup it contains simply never happen, and nothing at the call site says so.

The following code illustrates the pattern detected by this rule:

@implementation Importer
- (void)importOnSameLine:(NSData *)data {
    @try {
        [self decode:data];
    } @catch (id anyError) {
        [self log];
        // FLAGGED: @catch handler placed after a catch-all @catch (id)
    } @catch (NSException *exception) {
        [self reset];
    }
}

- (void)aLaterMethodWithItsOwnCatch:(NSData *)data {
    @try {
        [self decode:data];
    } @catch (NSException *exception) {
        [self reset];
    }
}

- (void)decode:(NSData *)data { }
- (void)log { }
- (void)reset { }
@end

Remediation

Move the specific @catch above the @catch (id …​), so each handler is reachable and the catch-all keeps its role as the last resort. If the specific clause is genuinely obsolete, delete it rather than leaving it below.

Configuration

This detector does not need any configuration.

© 2026 Xygeni. All rights reserved