Skip to content

Regression in 0.15.1: valid out-of-order child table raises KeyAlreadyPresent #571

Description

@satriadhikara

Summary

Tomlkit 0.15.1 rejects a valid document when a table contains an out-of-order
child, its concrete parent is declared afterward, and another table separates a
later sibling child.

This is a regression: tomllib and Tomlkit 0.13.3, 0.14.0, and 0.15.0 all
accept the document.

Minimal reproduction

import tomlkit

source = """\
[tool.ruff]
[tool.ruff.lint.a]
[tool.ruff.lint]
[[tool.poetry.source]]
[tool.ruff.lint.b]
"""

tomlkit.loads(source)

Actual result

tomlkit.exceptions.ParseError: Key "lint" already exists. at line 5 col 0

Expected result

The document parses successfully, matching tomllib:

{
    "tool": {
        "ruff": {"lint": {"a": {}, "b": {}}},
        "poetry": {"source": [{}]},
    }
}

Each header in the minimized example is necessary to trigger the failure. The
array-of-tables is not special: replacing [[tool.poetry.source]] with any
intervening table header also reproduces it, while removing the intervening
header makes it pass.

Regression boundary

Reproduced on Python 3.13.8 and 3.14.2:

  • stdlib tomllib: passes
  • Tomlkit 0.13.3: passes
  • Tomlkit 0.14.0: passes
  • Tomlkit 0.15.0: passes
  • Tomlkit 0.15.1 and current master: fail

Suspected root cause

The regression appears to come from the concrete+super validation added in
#530 / commit d3c76f0.

When [tool.ruff.lint.b] is appended, the existing lint entry is represented
by OutOfOrderTableProxy, while the candidate is a Table.
_validate_table_candidate() checks:

isinstance(existing, (Table, AoT)) != isinstance(v, (Table, AoT))

The proxy is table-like but fails the left-hand isinstance check, so valid
input is reported as a type conflict and raises KeyAlreadyPresent("lint").

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions