Skip to content

Duplicate extend key in generated ruff config causes hatch check to fail #2304

@benediktziegler

Description

@benediktziegler

When a project has a ruff.toml or a pyproject.toml with [tool.ruff] that already contains an extend key, running hatch check code (or hatch check fmt) crashes with a TOML parse error.

hatch copies the user's ruff config into an internal working directory and unconditionally prepends/inserts extend = "<internal_defaults>" without checking whether extend is already present. This produces a duplicate key, which is invalid TOML.

Steps to reproduce

With ruff.toml:

# ruff.toml
extend = "ruff_defaults.toml"
line-length = 100
# hatch.toml
[envs.hatch-check-code]
# no config-path set
$ hatch check code

With pyproject.toml:

# pyproject.toml
[tool.ruff]
extend = "ruff_defaults.toml"
line-length = 100
$ hatch check code

Expected behavior

hatch check code runs successfully. The existing extend key is respected and no duplicate is inserted.

Actual behavior

ruff failed
  Cause: Failed to load configuration `/home/<user>/.local/share/hatch/env/.internal/hatch-check-code/.config/<id>/ruff.toml`
  Cause: Failed to parse /home/<user>/.local/share/hatch/env/.internal/hatch-check-code/.config/<id>/ruff.toml
  Cause: TOML parse error at line 2, column 1
  |
2 | extend = "ruff_defaults.toml"
  | ^^^^^^
duplicate key

Workaround

Set config-path explicitly in hatch.toml for the affected environments:

[envs.hatch-check-code]
config-path = "ruff_defaults.toml"

[envs.hatch-check-fmt]
config-path = "ruff_defaults.toml"

This takes an early-return path in write_config_file that skips the injection entirely.

Possible fix

Before inserting, check whether extend already exists in the relevant section/file and skip the insertion if so.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions