astral-sh/ruff · error

cannot contain newline characters

Error message

{flag} <reason> cannot contain newline characters

What it means

When `check` runs with a suppression comment flag that takes a reason (e.g. `--per-file-ignores`-style suppression with `--...-reason`), the reason string is embedded into generated suppression comments. A reason containing newlines would break comment formatting, so check rejects it with this error.

Solutions

  1. Replace newlines in the reason with spaces before passing it to the flag.
  2. Collapse multi-line messages with `tr '\n' ' '` or equivalent in shell scripts.
  3. Check quoting/substitution in CI variables that supply the reason.

Example fix

// before
ruff check --fixable-reason="first line
second line" .
// after
REASON=$(echo "$MSG" | tr '\n' ' ')
ruff check --fixable-reason="$REASON" .
Defensive patterns

Strategy: validation

Validate before calling

REASON=$(printf '%s' "$REASON" | tr '\r\n' '  ')
case "$REASON" in *$'\n'*|*$'\r'*) echo "reason must be single-line"; exit 2;; esac

Type guard

fn is_single_line(s: &str) -> bool { !s.contains(['\n', '\r']) }

Try / catch

match run_check(suppression_reason) {
    Err(e) if e.to_string().ends_with("cannot contain newline characters") => {
        eprintln!("Flatten the reason to a single line before passing it");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Running `ruff check` with a suppression flag such as `--extend-suppress`/`--suppression-reason`-style argument whose value contains `\n` or `\r`, and fix mode is not generating the fix (the newline check applies regardless).

Common situations: Shell scripts or CI matrices building the reason from multi-line strings, accidental shell quoting that injects a newline, or YAML/env substitution producing multiline values.

Related errors


AI-assisted analysis of astral-sh/ruff@26f38c119c (2026-09-05). Data as JSON: /api/errors/cd93b4234f778b26. Report an issue: GitHub.

Appendix: source

Thrown at crates/ruff/src/lib.rs:338

        warn_user!("Detected debug build without --no-cache.");
    }

    let suppression = cli
        .add_noqa
        .as_ref()
        .map(|reason| (reason, SuppressionKind::Noqa, "--add-noqa"))
        .or_else(|| {
            cli.add_ignore
                .as_ref()
                .map(|reason| (reason, SuppressionKind::Ignore, "--add-ignore"))
        });

    if let Some((reason, suppression_kind, flag)) = suppression {
        if !fix_mode.is_generate() {
            warn_user!("--fix is incompatible with {flag}.");
        }
        if reason.contains(['\n', '\r']) {
            return Err(anyhow::anyhow!(
                "{flag} <reason> cannot contain newline characters"
            ));
        }

        let reason_opt = (!reason.is_empty()).then_some(reason.as_str());

        let modifications = commands::add_noqa::add_noqa(
            &files,
            &pyproject_config,
            &config_arguments,
            reason_opt,
            suppression_kind,
        )?;
        if modifications > 0 && config_arguments.log_level >= LogLevel::Default {
            let s = if modifications == 1 { "" } else { "s" };
            let suppression = match suppression_kind {
                SuppressionKind::Noqa => "noqa directive",
                SuppressionKind::Ignore => "ignore comment",

View on GitHub (pinned to 26f38c119c)