Skip to content

ops.Relation.load() on unit data results in JSONDecodeError: Extra data #2591

Description

@dsloanm

Description

When ops.Relation.load() is used to load from unit data into a data class, a json.decoder.JSONDecodeError: Extra data is raised.

I believe this is due to the default json.loads() decoder failing to interpret the standard values in the unit data, e.g. {'egress-subnets': '10.200.245.189/32', 'ingress-address': '10.200.245.189', 'private-address': '10.200.245.189'}.

To reproduce

The error can be reproduced by this charm code, which save()s then load()s unit data in a peer relation:

class PeerData(pydantic.BaseModel):
    myflag: bool = False

class ReloadIssueCharm(ops.CharmBase):
    """Charm the application."""

    def __init__(self, framework: ops.Framework):
        super().__init__(framework)
        framework.observe(self.on.start, self._on_start)

    def _on_start(self, _: ops.StartEvent):
        if peers := self.model.get_relation("peers"):
            save_data = PeerData(myflag=True)
            peers.save(save_data, self.unit)
            logger.info("Saved peer data: %s", save_data)

            load_data = peers.load(PeerData, self.unit)
            logger.info("Loaded peer data: %s", load_data)

The _on_start handler fails at peers.load(PeerData, self.unit):

`json.decoder.JSONDecodeError: Extra data` traceback
2026-07-01 09:58:38 INFO unit.reload-issue-charm/0.juju-log server.go:405 Saved peer data: myflag=True
2026-07-01 09:58:38 ERROR unit.reload-issue-charm/0.juju-log server.go:405 Uncaught exception while in charm code:
Traceback (most recent call last):
  File "/var/lib/juju/agents/unit-reload-issue-charm-0/charm/src/charm.py", line 39, in <module>
    ops.main(ReloadIssueCharm)
  File "/var/lib/juju/agents/unit-reload-issue-charm-0/charm/venv/lib/python3.12/site-packages/ops/__init__.py", line 360, in __call__
    return _main.main(charm_class=charm_class, use_juju_for_storage=use_juju_for_storage)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/lib/juju/agents/unit-reload-issue-charm-0/charm/venv/lib/python3.12/site-packages/ops/_main.py", line 507, in main
    manager.run()
  File "/var/lib/juju/agents/unit-reload-issue-charm-0/charm/venv/lib/python3.12/site-packages/ops/_main.py", line 491, in run
    self._emit()
  File "/var/lib/juju/agents/unit-reload-issue-charm-0/charm/venv/lib/python3.12/site-packages/ops/_main.py", line 426, in _emit
    self._emit_charm_event(self.dispatcher.event_name)
  File "/var/lib/juju/agents/unit-reload-issue-charm-0/charm/venv/lib/python3.12/site-packages/ops/_main.py", line 470, in _emit_charm_event
    event_to_emit.emit(*args, **kwargs)
  File "/var/lib/juju/agents/unit-reload-issue-charm-0/charm/venv/lib/python3.12/site-packages/ops/framework.py", line 337, in emit
    framework._emit(event)
  File "/var/lib/juju/agents/unit-reload-issue-charm-0/charm/venv/lib/python3.12/site-packages/ops/framework.py", line 910, in _emit
    self._reemit(event_path)
  File "/var/lib/juju/agents/unit-reload-issue-charm-0/charm/venv/lib/python3.12/site-packages/ops/framework.py", line 1028, in _reemit
    custom_handler(event)
  File "/var/lib/juju/agents/unit-reload-issue-charm-0/charm/src/charm.py", line 32, in _on_start
    load_data = peers.load(PeerData, self.unit)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/lib/juju/agents/unit-reload-issue-charm-0/charm/venv/lib/python3.12/site-packages/ops/model.py", line 1829, in load
    value = decoder(value)
            ^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/json/__init__.py", line 346, in loads
    return _default_decoder.decode(s)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/json/decoder.py", line 340, in decode
    raise JSONDecodeError("Extra data", s, end)
json.decoder.JSONDecodeError: Extra data: line 1 column 7 (char 6)

Or, more immediately:

>>> json.loads('10.200.245.189')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.12/json/__init__.py", line 346, in loads
    return _default_decoder.decode(s)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/json/decoder.py", line 340, in decode
    raise JSONDecodeError("Extra data", s, end)
json.decoder.JSONDecodeError: Extra data: line 1 column 7 (char 6)

Workaround

We are working around this with a custom decoder that interprets the values as strings:

def _unit_data_decoder(value: str) -> str:
    if not (value.startswith('"') and value.endswith('"')):
        value = f'"{value}"'
    return json.loads(value)

E.g. equivalent to json.loads('"10.200.245.189"').

Then load_data = peers.load(PeerData, self.unit, decoder=_unit_data_decoder) runs as expected:

2026-07-01 10:01:29 INFO unit.reload-issue-charm-workaround/0.juju-log server.go:405 Saved peer data: myflag=True
2026-07-01 10:01:29 INFO unit.reload-issue-charm-workaround/0.juju-log server.go:405 Loaded peer data: myflag=True

Expectation

ops.Relation.load() should be able to read unit data without without raising a JSONDecodeError and without need for a custom decoder.

Metadata

Metadata

Assignees

No one assigned

    Labels

    rainy daySmall items done in ~10% of each week's timesmall itemA small item, for some value of 'small'

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions