How do you suppress a single detekt finding in Kotlin source without editing detekt.yml?
answer
- It reuses a standard Kotlin annotation
- Rule id goes in the annotation string
- Smallest enclosing declaration, not the class
- Aliases like 'unused' also match
basics
~20 sAnnotate the smallest declaration that contains the finding with Kotlin's @Suppress, passing detekt's rule id — for example @Suppress("MagicNumber") on one function. detekt reuses the standard annotation; a configured alias or the rule set id also works.
solid answer
~40 sdetekt honours Kotlin's own `@Suppress` annotation, so a local exception is an annotation on the **smallest enclosing declaration** — the property, function, or class that contains the flagged code — for example `@Suppress("MagicNumber")` on a single function, or `@file:Suppress("MagicNumber")` at the top of a file when the whole file is the exception. The string is detekt's rule id; several rules also declare an `aliases` list in the config, which is why `@Suppress("unused")` silences `UnusedPrivateProperty` and `@Suppress("DEPRECATION")` silences the `Deprecation` rule. A rule set id such as `complexity` suppresses the whole set for that declaration, which is almost always too coarse. I treat every suppression as debt: keep it on the narrowest declaration and put a one-line comment next to it explaining why the rule does not apply here, so review can challenge it later.
code
kotlin · 11 lines@Suppress("MagicNumber") // protocol field offsets fixed by the spec
fun decodeHeader(bytes: ByteArray): Header =
Header(
version = bytes[0].toInt(),
flags = bytes[3].toInt(),
length = bytes[7].toInt(),
)
// Alias declared by the rule's config: silences UnusedPrivateProperty too
@Suppress("unused")
private val reservedForFutureUse = 0go deeper
Recall that detekt reuses Kotlin's @Suppress and that the string is the rule id shown in the report. Be able to place it on the offending function rather than the whole file.
Explain the resolution order: annotation on the nearest declaration, alias names from the rule's config, rule set ids as a coarse fallback, and why file-level targeting needs the @file: prefix.
Show judgment about which mechanism fits: one justified exception is an annotation, a category of files is a path exclude, and pre-existing violations are a different tool entirely. Treat unexplained suppressions as review findings.
Own the policy: how many suppressions a module may carry before the rule itself is considered mis-tuned, and how suppression density is surfaced so standards stay a decision rather than an accumulation of quiet holes.
## The mechanism detekt does not invent its own off-switch syntax. It reads Kotlin's standard `@Suppress` annotation, the same one the Kotlin compiler and IntelliJ inspections use, and skips any finding whose location sits inside a declaration annotated with a name that matches the reporting rule. That is the whole model: there is nothing to learn beyond *which name to pass* and *where to put the annotation*. ## Which name to pass Three kinds of string work: - **The rule id** — the exact rule name as it appears in the report and in `detekt.yml`: `MagicNumber`, `LongMethod`, `SwallowedException`, `TooGenericExceptionCaught`. This is the normal choice. - **A configured alias.** Many built-in rules ship with an `aliases` list in the default configuration so that the names people already use from IntelliJ keep working. `UnusedPrivateProperty`, `UnusedPrivateFunction` and `UnusedPrivateClass` all declare the alias `unused`; `UnusedParameter` declares `UNUSED_PARAMETER` and `unused`; the `Deprecation` rule declares `DEPRECATION`. So an annotation you added for the compiler often silences the matching detekt rule as a side effect — useful, and occasionally surprising when a rule goes quiet for a reason you did not intend. - **The rule set id** — `complexity`, `style`, `naming`, `potential-bugs`, `exceptions`, `performance`, `coroutines`, `empty-blocks`, `comments`. This silences every rule in that set for the annotated declaration. It is a blunt instrument and rarely the right answer in review. ## Where to put the annotation On the **nearest enclosing declaration** — the function, property, constructor, class, or object that contains the flagged element. Kotlin does not let you annotate an arbitrary statement in the middle of a function body, so the practical granularity is one declaration. If genuinely the whole file is an exception (a generated file, a table of protocol constants), the file-level target `@file:Suppress("MagicNumber")` goes above the `package` line. The common mistake is annotating too high: putting `@Suppress("LongMethod")` on the class silences it for every function in that class, including ones written next month. Push the annotation as far down as the language allows. ## Suppression versus configuration The annotation is for a **deliberate, local, justified** exception — the one place where the idiom really does not apply. Two other tools cover different cases, and mixing them up is what an interviewer listens for: - A rule that is wrong for a *whole category of files* (test sources, generated code) belongs in that rule's `excludes` list of path globs in `detekt.yml`, not in a hundred annotations. - A rule that is right but has *pre-existing* violations you do not want to fix today is what a baseline file is for. Reaching for `@Suppress` where one of those fits produces annotation litter that nobody dares remove. ## Practices that survive review Always pair the annotation with a short comment saying *why*: `@Suppress("MagicNumber") // wire-format field offsets, defined by the spec`. A suppression with a reason is a decision; a bare one is an unexplained hole in the standard. Grep for `@Suppress` occasionally — a growing count in a module usually means a rule is mis-tuned for that code and should be configured properly instead. Finally, remember that a suppressed finding is genuinely gone: it is not reported, not counted, and does not appear in the HTML, XML or SARIF output. Nothing downstream will remind you it exists, which is exactly why the comment matters.
- Why does @Suppress("unused") sometimes silence a detekt finding you never targeted?Several detekt rules declare alias names in their configuration so that familiar IntelliJ suppression strings keep working. `UnusedPrivateProperty`, `UnusedPrivateFunction`, `UnusedPrivateClass`, `UnusedParameter` and `UnusedVariable` all list `unused` as an alias, so one annotation added for the IDE quietly covers those rules too.
- When is a rule's excludes list the better answer than an annotation?When the exception is a whole category of files rather than one decision. Test sources, generated code and script files usually justify a path glob such as `excludes: ['**/test/**']` on the rule in `detekt.yml`. That is one reviewable line instead of dozens of annotations that nobody will ever dare delete.
It is the same key as the compiler's own mute button — detekt just answers to extra names, and you want to mute one instrument, not the orchestra.
saying these in an interview costs you the question
- Thinking a trailing comment can disable a detekt rule
- Putting @Suppress on the class instead of the function
- Using @Suppress for pre-existing debt across the codebase
- Suppressing a whole rule set id for one finding
- Adding suppressions with no justification comment