Problem
Getting the worker to boot took four rounds of reactive pinning, each found by
reading a traceback from a failed deploy:
protobuf too old for Modal's injected client → container crash-loop
huggingface_hub 1.x dropped proxies/resume_download, breaking
sam-audio's BaseModel._from_pretrained
- Pinning hub
<1.0 then broke transformers 5.x, which imports
is_offline_mode and needs hub >=1.0
- Resolved by holding
transformers in the 4.x line
The image now carries three hand-written constraints:
.pip_install("protobuf>=5.27,<7", "huggingface_hub>=0.26,<1.0", "transformers>=4.54,<5")
Why this is fragile
These are floating ranges layered over sam-audio's own floating ranges.
sam-audio asks for transformers>=4.54 with no ceiling and says nothing about
huggingface_hub. It also pulls four git dependencies — dacvae, imagebind,
laion-clap, perception-models — each unpinned and tracking a moving branch.
A rebuild months from now resolves differently and breaks in a new way. The
build is not reproducible, and nothing detects that until a deploy fails.
The real fix
Compile a fully-resolved lockfile and build from it:
uv pip compile modal-requirements.in -o modal-requirements.txt --generate-hashes
Then .pip_install_from_requirements("modal-requirements.txt"). Every version
is explicit and hash-checked; upgrades become a deliberate recompile with a
reviewable diff, not a surprise at deploy time.
Git dependencies need pinning to commit SHAs, not branches.
Done when
Two builds a month apart produce identical resolved versions, and a dependency
change shows up as a lockfile diff in review.
Problem
Getting the worker to boot took four rounds of reactive pinning, each found by
reading a traceback from a failed deploy:
protobuftoo old for Modal's injected client → container crash-loophuggingface_hub1.x droppedproxies/resume_download, breakingsam-audio's
BaseModel._from_pretrained<1.0then broketransformers5.x, which importsis_offline_modeand needs hub>=1.0transformersin the 4.x lineThe image now carries three hand-written constraints:
Why this is fragile
These are floating ranges layered over sam-audio's own floating ranges.
sam-audio asks for
transformers>=4.54with no ceiling and says nothing abouthuggingface_hub. It also pulls four git dependencies —dacvae,imagebind,laion-clap,perception-models— each unpinned and tracking a moving branch.A rebuild months from now resolves differently and breaks in a new way. The
build is not reproducible, and nothing detects that until a deploy fails.
The real fix
Compile a fully-resolved lockfile and build from it:
Then
.pip_install_from_requirements("modal-requirements.txt"). Every versionis explicit and hash-checked; upgrades become a deliberate recompile with a
reviewable diff, not a surprise at deploy time.
Git dependencies need pinning to commit SHAs, not branches.
Done when
Two builds a month apart produce identical resolved versions, and a dependency
change shows up as a lockfile diff in review.