canonical / canonical/operator
`relation-get`/`relation-list` on a gone relation raise an opaque `ModelError("permission denied")` instead of `RelationNotFoundError`
- Dominant language
- Python
- Stars
- 267
- Forks
- 136
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 28
Description
Hi mates!
I'm troubleshooting [this error in opentelemetry-collector-k8s](https://github.com/canonical/opentelemetry-collector-k8s-operator/issues/355), and as far as I understand there is room to improve the way ops works with missing remote relations.
Juju's uniter facade returns `permission denied` (not `relation not found`) for hook commands on a relation that no longer exists in state (cross-model relation in teardown, `remove-saas`, `remove-relation --force`, app removed with `--force`).
ops maps only the "relation not found" flavor to `RelationNotFoundError` ([`_wrap_hookcmd`, `ops/model.py#L3596`](https://github.com/canonical/operator/blob/d9d03ab5f987cfe52270c06266e2249d4ca41b8f/ops/model.py#L3596)), which is handled gracefully:
- [`Relation.__init__` catches it → `active = False`](https://github.com/canonical/operator/blob/d9d03ab5f987cfe52270c06266e2249d4ca41b8f/ops/model.py#L1715-L1717) (dead relation, no units);
- [`RelationDataContent._load` catches it → returns `{}`](https://github.com/canonical/operator/blob/d9d03ab5f987cfe52270c06266e2249d4ca41b8f/ops/model.py#L2001-L2012) ("dead relations tell no tales").
The "permission denied" flavor surfaces as a plain `ModelError`, which:
- crashes `Relation.__init__` ([the `relation_list` call at `ops/model.py#L1709`](https://github.com/canonical/operator/blob/d9d03ab5f987cfe52270c06266e2249d4ca41b8f/ops/model.py#L1709)), failing the whole endpoint's relation list construction ([`RelationMapping.__getitem__`](https://github.com/canonical/operator/blob/d9d03ab5f987cfe52270c06266e2249d4ca41b8f/ops/model.py#L941)), and
- crashes remote application databag reads ([`RelationDataContent._load` → `relation_get`](https://github.com/canonical/operator/blob/d9d03ab5f987cfe52270c06266e2249d4ca41b8f/ops/model.py#L2001-L2012)).
## References
- canonical/operator#2575: better context for ModelError
- canonical/operator#745: dangling relations produce inconsistent errors
- Juju facade: [`getRemoteRelationAppSettings` returns `ErrPerm` for missing relations](https://github.com/juju/juju/blob/8f61c6be244b734bf669819351ff7601d0425876/apiserver/facades/agent/uniter/uniter.go#L1802)
- canonical/opentelemetry-collector-k8s-operator#355: production crash that motivated this
Contributor guide
Research direction
Start in ops/model.py at _wrap_hookcmd, Relation.__init__, and RelationDataContent._load, then trace the relation_list and relation_get calls described in the issue. Check how the existing RelationNotFoundError handling behaves for gone relations. Done means both permission-denied cases follow the same graceful dead-relation behavior instead of surfacing as a plain ModelError.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 74/100