Skip to content

KeyAlreadyPresent when accessing valid TOML with repeated array tables under an out-of-order table #505

@chibhat-orcl

Description

@chibhat-orcl

Description

tomlkit.parse() accepts a TOML document, but later accessing a parsed table with doc.get("hooks") can raise:

tomlkit.exceptions.KeyAlreadyPresent: Key "Stop" already exists.

This happened with a valid TOML config that contains repeated array tables such as [[hooks.Stop]] plus sibling/subtables under [hooks].

The failure appears to happen while constructing an OutOfOrderTableProxy, not during parsing.

Observed behavior
This code path fails:

import tomlkit
from pathlib import Path

text = Path("config.toml").read_text(encoding="utf-8")

doc = tomlkit.parse(text)      # succeeds
hooks = doc.get("hooks")       # raises KeyAlreadyPresent

Traceback:

Traceback (most recent call last):
  File "<string>", line 11, in <module>
  File "<frozen _collections_abc>", line 807, in get
  File ".../tomlkit/container.py", line 711, in __getitem__
    item = self.item(key)
  File ".../tomlkit/container.py", line 517, in item
    return OutOfOrderTableProxy(self, idx)
  File ".../tomlkit/container.py", line 912, in __init__
    self._internal_container._raw_append(k, v)
  File ".../tomlkit/container.py", line 360, in _raw_append
    raise KeyAlreadyPresent(key)
tomlkit.exceptions.KeyAlreadyPresent: Key "Stop" already exists.

Expected behavior

If tomlkit.parse() accepts the TOML document, then accessing doc.get("hooks") should not fail with KeyAlreadyPresent.

Repeated array tables like this are valid TOML:

[hooks]

[[hooks.Stop]]
matcher = ".*"

[[hooks.Stop.hooks]]
type = "command"
command = "one"
# even some actual content in here is valid but tomlkit gives an issue if something else is present
[[hooks.Stop]]
matcher = ".*"

[[hooks.Stop.hooks]]
type = "command"
command = "two"

[hooks.state]

[hooks.state.example]
trusted_hash = "sha256:abc"

Actual behavior
On the original config, tomlkit.parse(text) succeeded, but doc.get("hooks") raised KeyAlreadyPresent: Key "Stop" already exists.

Versions tested
This reproduces with:

Python 3.12.7
tomlkit 0.14.0
tomlkit 0.15.0
tomlkit 0.15.0 produced the same class of traceback:

0.15.0
tomlkit.exceptions.KeyAlreadyPresent: Key "Stop" already exists.

Notes
The issue seems related to OutOfOrderTableProxy handling repeated array tables under a table that also contains sibling tables/subtables. Reordering the TOML so repeated [[hooks.Stop]] entries are grouped before other sibling tables avoids the failure in my application, but the original TOML should still be accessible after parsing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    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