amir20/dozzle · error
matches multiple containers: . To act on the right one
Error message
%q matches multiple containers: %s. To act on the right one, %s
What it means
ambiguousError is produced by resolveContainerRef when a container name/id prefix matches multiple containers. It lists each candidate (host-qualified) and adds a context-specific hint telling the LLM caller exactly how to disambiguate, since acting on the wrong container would be destructive.
Solutions
- Re-issue the call passing host_id to scope to a single host (per the hint when candidates span hosts)
- Pass the full container id or the exact full container name instead of a prefix/short name
- Clean up stale stopped containers so only one live match remains
Example fix
// before resolveContainerRef(ctx, deps, "web") // after resolveContainerRef(ctx, deps, "a1b2c3d4e5f6") // full id, or pass host_id: "abc123" alongside the name
Defensive patterns
Strategy: validation
Validate before calling
matches := findContainersByName(ref)
if len(matches) > 1 { resolve with host_id or full id before acting } Type guard
func isFullContainerID(ref string) bool {
return len(ref) >= 32 && !strings.Contains(ref, "/")
} Prevention
- Use full container ids in tool calls
- Scope by host_id when multiple hosts are registered
- Prune stopped duplicate containers after Swarm redeploys
When it happens
Trigger: resolveContainerRef's name lookup returns several containers and the runningContainers tie-breaker did not reduce them to one; raised in the final return of ambiguousError. Triggered by tool calls using a short name or id prefix shared by multiple containers (possibly across hosts).
Common situations: Swarm redeploy leaves old and new replicas of the same service name; duplicate container names across hosts in a multi-host setup; id-prefix that is too short to be unique.
Related errors
- no container matching
- host name is ambiguous; matches: . Pass the host id instead
- notifications are not configured on this host
- update failed
- unknown action
AI-assisted analysis of amir20/dozzle@d9463cbe21 (2026-09-07).
Data as JSON: /api/errors/02ab1a3346e10b1f.
Report an issue: GitHub.
Appendix: source
Thrown at internal/cloud/tools_resolve.go:259
parts := make([]string, len(candidates))
sameHost := true
for i, c := range candidates {
parts[i] = fmt.Sprintf("%s (id %s, %s, on host %s)", c.Name, shortID(c.ID), describeState(c), resolveHostName(c.Host, hostNames))
if c.Host != candidates[0].Host {
sameHost = false
}
}
var hint string
switch {
case hostRef != "" || sameHost:
// A host was already supplied, or every candidate is on the same host —
// scoping by host_id cannot narrow it further.
hint = "pass the exact container id or the full container name to disambiguate"
default:
hint = "pass host_id to scope to one host, or pass the exact container id"
}
return fmt.Errorf("%q matches multiple containers: %s. To act on the right one, %s", containerRef, strings.Join(parts, "; "), hint)
}
// runningContainers returns only the candidates in the running state. It breaks
// a multi-match tie down to the single live container when every other candidate
// is a stopped corpse (the typical Swarm-redeploy / restart-churn case), which
// is the one situation where picking among matches is unambiguous and safe.
func runningContainers(cs []container.Container) []container.Container {
live := make([]container.Container, 0, len(cs))
for _, c := range cs {
if strings.EqualFold(c.State, "running") {
live = append(live, c)
}
}
return live
}
// bestCandidate picks the single most-relevant container from an ambiguous name
// match for the read-only path. Ordering: running beats stopped, then newestView on GitHub (pinned to d9463cbe21)