Skip to content

Commit 223deb9

Browse files
authored
fix(nats): improve error handling (#9222)
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
1 parent 9f8821b commit 223deb9

3 files changed

Lines changed: 37 additions & 4 deletions

File tree

core/cli/worker.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -692,13 +692,13 @@ func (s *backendSupervisor) subscribeLifecycleEvents() {
692692

693693
// backend.delete — stop backend + delete files (request-reply)
694694
s.nats.SubscribeReply(messaging.SubjectNodeBackendDelete(s.nodeID), func(data []byte, reply func([]byte)) {
695-
xlog.Info("Received NATS backend.delete event")
696695
var req messaging.BackendDeleteRequest
697696
if err := json.Unmarshal(data, &req); err != nil {
698697
resp := messaging.BackendDeleteReply{Success: false, Error: fmt.Sprintf("invalid request: %v", err)}
699698
replyJSON(reply, resp)
700699
return
701700
}
701+
xlog.Info("Received NATS backend.delete event", "backend", req.Backend)
702702

703703
// Stop if running this backend
704704
if s.isRunning(req.Backend) {

core/gallery/backends.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -300,14 +300,29 @@ func DeleteBackendFromSystem(systemState *system.SystemState, name string) error
300300

301301
backend, ok := backends.Get(name)
302302
if !ok {
303-
return fmt.Errorf("backend %q: %w", name, ErrBackendNotFound)
303+
// Not found by direct key — try matching by gallery name (metadata.Name)
304+
// The UI may send gallery-style names like "localai@llama-cpp" which
305+
// don't match the directory-based keys used in the backends map.
306+
for _, b := range backends {
307+
if b.Metadata != nil && b.Metadata.Name == name && !b.IsMeta {
308+
backend = b
309+
ok = true
310+
break
311+
}
312+
}
313+
if !ok {
314+
return fmt.Errorf("backend %q: %w", name, ErrBackendNotFound)
315+
}
304316
}
305317

306318
if backend.IsSystem {
307319
return fmt.Errorf("system backend %q cannot be deleted", name)
308320
}
309321

310-
backendDirectory := filepath.Join(systemState.Backend.BackendsPath, name)
322+
// Use the backend's actual Name (directory key) for path resolution,
323+
// not the caller-supplied name which may be a gallery-style name.
324+
dirName := backend.Name
325+
backendDirectory := filepath.Join(systemState.Backend.BackendsPath, dirName)
311326

312327
// check if the backend dir exists
313328
if _, err := os.Stat(backendDirectory); os.IsNotExist(err) {
@@ -325,7 +340,7 @@ func DeleteBackendFromSystem(systemState *system.SystemState, name string) error
325340
if err != nil {
326341
return err
327342
}
328-
if metadata != nil && metadata.Alias == name {
343+
if metadata != nil && (metadata.Alias == name || metadata.Alias == dirName) {
329344
backendDirectory = filepath.Join(systemState.Backend.BackendsPath, backend.Name())
330345
foundBackend = true
331346
break

core/services/nodes/managers_distributed.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/mudler/LocalAI/core/services/galleryop"
1212
"github.com/mudler/LocalAI/pkg/model"
1313
"github.com/mudler/xlog"
14+
"github.com/nats-io/nats.go"
1415
)
1516

1617
// DistributedModelManager wraps a local ModelManager and adds NATS fan-out
@@ -84,6 +85,13 @@ func (d *DistributedBackendManager) DeleteBackend(name string) error {
8485
continue
8586
}
8687
if _, delErr := d.adapter.DeleteBackend(node.ID, name); delErr != nil {
88+
if errors.Is(delErr, nats.ErrNoResponders) {
89+
// Node's NATS subscription is gone — likely restarted with a new ID.
90+
// Mark it unhealthy so future fan-outs skip it.
91+
xlog.Warn("No NATS responders for node, marking unhealthy", "node", node.Name, "nodeID", node.ID)
92+
d.registry.MarkUnhealthy(context.Background(), node.ID)
93+
continue
94+
}
8795
xlog.Warn("Failed to propagate backend deletion to worker", "node", node.Name, "backend", name, "error", delErr)
8896
errs = append(errs, fmt.Errorf("node %s: %w", node.Name, delErr))
8997
}
@@ -105,6 +113,11 @@ func (d *DistributedBackendManager) ListBackends() (gallery.SystemBackends, erro
105113
}
106114
reply, err := d.adapter.ListBackends(node.ID)
107115
if err != nil {
116+
if errors.Is(err, nats.ErrNoResponders) {
117+
xlog.Warn("No NATS responders for node, marking unhealthy", "node", node.Name, "nodeID", node.ID)
118+
d.registry.MarkUnhealthy(context.Background(), node.ID)
119+
continue
120+
}
108121
xlog.Warn("Failed to list backends on worker", "node", node.Name, "error", err)
109122
continue
110123
}
@@ -145,6 +158,11 @@ func (d *DistributedBackendManager) InstallBackend(ctx context.Context, op *gall
145158
}
146159
reply, err := d.adapter.InstallBackend(node.ID, backendName, "", string(galleriesJSON))
147160
if err != nil {
161+
if errors.Is(err, nats.ErrNoResponders) {
162+
xlog.Warn("No NATS responders for node, marking unhealthy", "node", node.Name, "nodeID", node.ID)
163+
d.registry.MarkUnhealthy(context.Background(), node.ID)
164+
continue
165+
}
148166
xlog.Warn("Failed to install backend on worker", "node", node.Name, "backend", backendName, "error", err)
149167
continue
150168
}

0 commit comments

Comments
 (0)