Skip to content

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

Description

@chrisblech

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.:

   "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).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions