.agents/debugging-backends.md
When a backend fails at runtime (e.g. a gRPC method error, a Python import error, or a dependency conflict), use this guide to diagnose, fix, and rebuild.
backend/python/<name>/ (or backend/go/<name>/, backend/cpp/<name>/)backends/<name>/ — this is what LocalAI actually runs. It is populated by make backends/<name> which builds a Docker image, exports it, and installs it via local-ai backends install.backends/<name>/venv/ — the installed Python venv (for Python backends). The Python binary is at backends/<name>/venv/bin/python.Editing files in backend/python/<name>/ does not affect the running backend until you rebuild with make backends/<name>.
Backend gRPC processes log to LocalAI's stdout/stderr. Look for lines tagged with the backend's model ID:
GRPC stderr id="trl-finetune-127.0.0.1:37335" line="..."
Common error patterns:
pkg/model/initializers.go) always calls LoadModel after Health; fine-tuning backends must implement it even as a no-op stub.AttributeError — usually a dependency version mismatch (e.g. pyarrow removing PyExtensionType).You can run the installed venv's Python to check imports without starting the full server:
backends/<name>/venv/bin/python -c "import datasets; print(datasets.__version__)"
If pip is missing from the venv, bootstrap it:
backends/<name>/venv/bin/python -m ensurepip
Then use backends/<name>/venv/bin/python -m pip install ... to test fixes in the installed venv before committing them to the source requirements.
When you hit a dependency conflict, check what the main library expects. For example, TRL's upstream requirements.txt:
https://github.com/huggingface/trl/blob/main/requirements.txt
Pin minimum versions in the backend's requirements files to match upstream.
If the Go side calls a method the backend doesn't implement (e.g. LoadModel), add a no-op stub in backend.py:
def LoadModel(self, request, context):
"""No-op — actual loading happens elsewhere."""
return backend_pb2.Result(success=True, message="OK")
The gRPC contract requires LoadModel to succeed for the model loader to return a usable client, even if the backend doesn't need upfront model loading.
Python backends often break when a transitive dependency releases a breaking change (e.g. pyarrow removing PyExtensionType). Steps:
backends/<name>/venv/bin/python -c "import <module>"backend/python/<name>/:
requirements.txt — base deps (grpcio, protobuf)requirements-cpu.txt — CPU-specific (includes PyTorch CPU index)requirements-cublas12.txt — CUDA 12requirements-cublas13.txt — CUDA 13make backends/<name>The Docker build uses uv for pip installs. When --extra-index-url points to the PyTorch wheel index, uv may refuse to fetch packages like requests from PyPI if it finds a different version on the PyTorch index first. Fix this by adding --index-strategy=unsafe-first-match to install.sh:
EXTRA_PIP_INSTALL_FLAGS+=" --upgrade --index-strategy=unsafe-first-match"
installRequirements
Most Python backends already do this — check backend/python/transformers/install.sh or similar for reference.
make backends/<name>
This runs the Docker build (Dockerfile.python), exports the image to backend-images/<name>.tar, and installs it into backends/<name>/. It also rebuilds the local-ai Go binary (without extra tags).
Important: If you were previously running with GO_TAGS=auth, the make backends/<name> step will overwrite your binary without that tag. Rebuild the Go binary afterward:
GO_TAGS=auth make build
After rebuilding a backend, you must restart LocalAI for it to pick up the new backend files. The backend gRPC process is spawned on demand when the model is first loaded.
# Kill existing process
kill <pid>
# Restart
./local-ai run --debug [your flags]
For fast iteration on a Python backend's backend.py without a full Docker rebuild, you can edit the installed copy directly:
# Edit the installed copy
vim backends/<name>/backend.py
# Restart LocalAI to respawn the gRPC process
This is useful for testing but does not persist — the next make backends/<name> will overwrite it. Always commit fixes to the source in backend/python/<name>/.
After fixing and rebuilding:
Registering backend name="<name>" in the logs