#2797·app

uv.lock can silently resolve protobuf incompatible with generated event_pb2.py (ImportError: runtime_version)

Author: chrisblechCreated Aug 25, 2026Updated Aug 25, 2026

Summary

uv.lock can silently resolve protobuf to a version that is incompatible with the checked-in generated code in app/events/generated/event_pb2.py, breaking app, email_handler.py and job_runner.py at import time with:

ImportError: cannot import name 'runtime_version' from 'google.protobuf'

This is not a hypothetical: it reproduces on a clean checkout of master.

Root cause

  • app/events/generated/event_pb2.py is generated code that requires google.protobuf.runtime_version, which only exists starting with protobuf >= 4.25.
  • protobuf is not a direct dependency in pyproject.toml — it is only pulled in transitively through google-api-python-client ~= 1.12.3 (an ~2020-era pin) via its google-api-core dependency chain.
  • google-api-python-client ~= 1.12.3's transitive constraints permit (and, once nudged, actively prefer) protobuf < 4, i.e. the latest 3.x release (3.20.3), which does not have the runtime_version module at all.
  • Separately, app/email_utils.py does from cachetools import cached, TTLCache, but cachetools is also not declared as a direct dependency — it only happens to be present transitively (currently via google-auth).

Because protobuf and cachetools both float as undeclared/transitive dependencies, the exact versions that get resolved depend entirely on whatever else is going on in the rest of the dependency graph at lock time. The currently committed uv.lock happens to have protobuf==5.27.1, which works — but that is incidental, not guaranteed by pyproject.toml.

Reproduction (on a clean checkout of master)

Add any new, unrelated top-level dependency and re-lock, e.g.:

diff
   "google-auth-httplib2 ~= 0.0.4",
+  "cachetools ~= 5.3.3",
uv lock

Result:

Updated cachetools v4.1.1 -> v5.3.3
Updated google-api-core v1.22.2 -> v1.34.1
Updated google-auth v1.22.0 -> v2.47.0
Updated googleapis-common-protos v1.52.0 -> v1.73.0
Updated protobuf v5.27.1 -> v3.20.3

protobuf drops from 5.27.1 to 3.20.3. Booting the app (or running python -c "import email_handler") with this lockfile immediately fails with the ImportError above.

We hit this for real: adding a dependency for an unrelated feature on a fork triggered exactly this, taking down a production deployment until we tracked it down (the failure mode gives no indication that protobuf, of all things, is the problem).

Suggested fix

  • Declare protobuf as a direct dependency with a floor that matches what the generated event_pb2.py actually requires, e.g. protobuf >= 4.25, < 6.
  • Declare cachetools as a direct dependency (it's directly imported in app/email_utils.py), e.g. cachetools >= 5.0, < 6.
  • Bump google-api-python-client off the 2020-era ~= 1.12.3 pin to a current 2.x release (>= 2.100, < 3) and google-auth-httplib2 to >= 0.2.0, < 1.0 accordingly — the only call site (app/api/views/auth.py, googleapiclient.discovery.build("oauth2", "v2", ...)) is unaffected by the major version bump.

This resolves cleanly to stable (non-prerelease) versions across the board (protobuf 5.27.1, google-api-core 2.30.3) and removes the landmine for good, rather than just re-locking back to a lucky state.

I'll open a PR with this fix, verified locally (dependency resolution + container build + import smoke test for app, email_handler.py, job_runner.py, and the Google OAuth code path).