The weekly uv update job fails outright and lands no PR. From the run on 73b3c43e:
Handled error whilst updating protobuf: dependency_file_not_resolvable
Because modal>=1.4.2 depends on one of:
protobuf>=3.19,<4.24.0
protobuf>4.24.0,<7.0
and opentelemetry-proto>=1.44.0 depends on protobuf==7.35.1, we
can conclude that modal>=1.4.2 and opentelemetry-proto>=1.44.0 are
incompatible.
...
And because bernstein[otel] depends on
opentelemetry-exporter-otlp-proto-grpc>=1.41.1 and bernstein[modal]
depends on modal>=1.4.2, we can conclude that bernstein[modal] and
bernstein[otel] are incompatible.
And because your project requires bernstein[modal] and bernstein[otel],
we can conclude that your project's requirements are unsatisfiable.
opentelemetry-proto pins protobuf==7.35.1 from 1.41.1 onward; modal caps it below 7.0. There is no version of protobuf that satisfies both, so [modal] and [otel] cannot resolve together.
pyproject.toml already declares one such pair — uv sync --all-extras refuses with "Extras grpc and modal are incompatible with the declared conflicts". modal × otel is the same shape and is not declared, so instead of being a known-incompatible pair it reads to the resolver as an unsatisfiable project.
What makes this worth fixing ahead of its size: the updater aborts the whole job on that error, so no uv dependency update lands at all — not a grouped minor bump and not a security bump either. The last one to get through was the group on #4080, and only because it predates the protobuf attempt. A supply-chain gate nobody can push through is off, not strict.
Where to start
pyproject.toml — the [[tool.uv.conflicts]] entries (the grpc × modal pair is the pattern to follow) and the otel / modal optional-dependency groups. The failing job is .github/workflows/dependabot-updates runs under the uv ecosystem in .github/dependabot.yml.
What proves it
uv lock --check resolving after the change, and uv sync --extra modal / uv sync --extra otel each succeeding on their own. A declared conflict has to still let both extras be installed separately — the failure mode of a careless fix is declaring the conflict so broadly that one of them stops installing.
Then a test that pins the pair, in the module that already covers the extras layout, so a future extra that reintroduces the same protobuf clash fails in the suite rather than three weeks later in a dependabot log nobody reads. Name it for the property: something like test_conflicting_extras_are_declared_not_merely_unsatisfiable.
Out of scope
Do not resolve it by capping opentelemetry-* — the otel exporters are on a security-relevant upgrade path and pinning them back to keep modal happy trades the wrong thing. Do not drop either extra.
The decision that is yours
Whether modal and otel are genuinely mutually exclusive for our users, or whether modal should move to a vendored/subprocess boundary so the protobuf pin stops being shared at all. Declaring the conflict is the small correct fix and unblocks the updater today; the second answer is a larger change and would want its own issue. Say which one you took in the PR body.
The weekly
uvupdate job fails outright and lands no PR. From the run on73b3c43e:opentelemetry-protopinsprotobuf==7.35.1from 1.41.1 onward;modalcaps it below 7.0. There is no version of protobuf that satisfies both, so[modal]and[otel]cannot resolve together.pyproject.tomlalready declares one such pair —uv sync --all-extrasrefuses with "Extrasgrpcandmodalare incompatible with the declared conflicts".modal×otelis the same shape and is not declared, so instead of being a known-incompatible pair it reads to the resolver as an unsatisfiable project.What makes this worth fixing ahead of its size: the updater aborts the whole job on that error, so no
uvdependency update lands at all — not a grouped minor bump and not a security bump either. The last one to get through was the group on #4080, and only because it predates the protobuf attempt. A supply-chain gate nobody can push through is off, not strict.Where to start
pyproject.toml— the[[tool.uv.conflicts]]entries (the grpc × modal pair is the pattern to follow) and theotel/modaloptional-dependency groups. The failing job is.github/workflows/dependabot-updatesruns under theuvecosystem in.github/dependabot.yml.What proves it
uv lock --checkresolving after the change, anduv sync --extra modal/uv sync --extra oteleach succeeding on their own. A declared conflict has to still let both extras be installed separately — the failure mode of a careless fix is declaring the conflict so broadly that one of them stops installing.Then a test that pins the pair, in the module that already covers the extras layout, so a future extra that reintroduces the same protobuf clash fails in the suite rather than three weeks later in a dependabot log nobody reads. Name it for the property: something like
test_conflicting_extras_are_declared_not_merely_unsatisfiable.Out of scope
Do not resolve it by capping
opentelemetry-*— the otel exporters are on a security-relevant upgrade path and pinning them back to keepmodalhappy trades the wrong thing. Do not drop either extra.The decision that is yours
Whether
modalandotelare genuinely mutually exclusive for our users, or whethermodalshould move to a vendored/subprocess boundary so the protobuf pin stops being shared at all. Declaring the conflict is the small correct fix and unblocks the updater today; the second answer is a larger change and would want its own issue. Say which one you took in the PR body.