Skip to content

fix(watchdog): don't log optional Free() as an error when backend returns Unimplemented (#10602)#10607

Merged
mudler merged 2 commits into
mudler:masterfrom
Dennisadira:fix/free-unimplemented-watchdog-10602
Jun 30, 2026
Merged

fix(watchdog): don't log optional Free() as an error when backend returns Unimplemented (#10602)#10607
mudler merged 2 commits into
mudler:masterfrom
Dennisadira:fix/free-unimplemented-watchdog-10602

Conversation

@Dennisadira

Copy link
Copy Markdown
Contributor

Description

Fixes #10602.

In distributed mode, the frontend container logged WARN Error freeing GPU resources error=rpc error: code = Unimplemented when the watchdog evicted a remote model overnight.

Root cause

When the watchdog evicts a model, ModelLoader.deleteProcess calls the backend's gRPC Free() to release VRAM before stopping the process. Free is optional: backends that don't override it return gRPC Unimplemented — this includes the generated UnimplementedBackendServer stub, many Python/external backends, and a federation proxy in distributed mode (where the model is remote, process == nil, and freeing is actually done by the remoteUnloader).

That Unimplemented response is expected, not a fault — VRAM is reclaimed when the local process is stopped, or by the remote unloader for remote backends. Logging it as WARN Error freeing GPU resources made a benign, optional RPC look like a failure.

Note: this log was already non-fatal (it warns and continues), so it did not itself stop the container. It just happened to be the last visible line. This PR removes the misleading error; if a separate crash cause is involved, the full container logs would be needed to chase it.

Change

  • pkg/grpc/grpcerrors: add reusable IsUnimplemented(err) helper, following the package's existing code-based detection idiom (prefer the typed gRPC status code, fall back to the message across non-gRPC boundaries). Table tests included.
  • pkg/model/process.go: Free() returning Unimplemented is now logged at Debug ("backend doesn't implement Free; GPU release handled on process stop"); genuine failures still Warn. Free() is still attempted for every backend, so backends that do implement it are unaffected.

Testing

  • go test ./pkg/grpc/grpcerrors/... and go test ./pkg/model/... pass.
  • gofmt / go vet clean on the changed files.

🤖 Generated with Claude Code

…urns Unimplemented (mudler#10602)

When the watchdog evicts a model, deleteProcess calls the backend's gRPC
Free() to release VRAM before stopping the process. Free is optional:
backends that don't override it -- the generated UnimplementedBackendServer
stub, many Python/external backends, or a federation proxy in distributed
mode -- return gRPC Unimplemented. That is expected, not a failure: VRAM is
reclaimed when the local process is stopped, or by the remote unloader for
remote backends. Logging it as "WARN Error freeing GPU resources" made a
benign, optional RPC look like a fault (the alarming line in mudler#10602, seen
in distributed mode where the model is remote and Free hits a stub).

Treat gRPC Unimplemented from Free() as a no-op logged at Debug; genuine
failures still Warn. Free() is still attempted for every backend, so any
backend that does implement it is unaffected.

Add a reusable grpcerrors.IsUnimplemented helper following the package's
existing code-based detection idiom (prefer the typed status code, fall
back to the message across non-gRPC boundaries), with table tests.

Assisted-by: Claude:claude-opus-4-8 [Claude Code]

Signed-off-by: Adira Denis Muhando <dennisadira@gmail.com>
Comment thread pkg/model/process.go Outdated
if grpcerrors.IsUnimplemented(err) {
xlog.Debug("Backend does not implement Free(); GPU release handled on process stop", "model", s)
} else {
xlog.Warn("Error freeing GPU resources", "error", err, "model", s)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about moving to error at this point? It was a warning before because we were not doing any distinction, but we could treat all the others as real error now

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call — done in 8e520dd. Since the expected Unimplemented case is now split out to Debug, the remaining branch is a genuine failure to release VRAM, so it's logged at error level now.

Per review: now that the expected gRPC Unimplemented case is split out and
logged at Debug, any remaining Free() error is a genuine failure to release
VRAM, so surface it at error level instead of warn.

Assisted-by: Claude:claude-opus-4-8 [Claude Code]

Signed-off-by: Adira Denis Muhando <dennisadira@gmail.com>
@mudler
mudler merged commit 4ec39bb into mudler:master Jun 30, 2026
58 checks passed
@localai-bot localai-bot added the bug Something isn't working label Jul 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Error freeing GPU resources error=rpc error: code = Unimplemented

3 participants