amir20/dozzle · error
invalid host id
Error message
invalid host id: %s
What it means
downloadLogs expects each entry of the `id` path/query value to be a composite "host~containerId" token; it splits on "~" and requires exactly 2 parts. A 400 "invalid host id: %s" is returned when the separator is missing or there is more than one "~".
Solutions
- Build the identifier as `${hostId}~${containerId}` before calling the endpoint.
- Trim and re-check stored IDs/URLs for stale or malformed host~id values.
- Use the container object from the API (which already carries host and id fields) instead of hand-assembling strings.
Example fix
// before
fetch(`/api/hosts/${container.id}/download`)
// after
fetch(`/api/hosts/${container.host}~${container.id}/download`) Defensive patterns
Strategy: validation
Validate before calling
function isCompositeId(id) {
const parts = String(id).split('~');
return parts.length === 2 && parts[0].length > 0 && parts[1].length > 0;
}
if (!isCompositeId(id)) throw new Error(`invalid host id: ${id}`) Prevention
- Always derive identifiers from the container API object (host + id fields), never from stored strings.
- Validate host~id format before building URLs.
- Beware separators: container ids never contain ~, but verify anyway.
When it happens
Trigger: GET download endpoint with ids like "abc123" (no host prefix), "host~a~b", or an empty id, producing len(strings.Split(id, "~")) != 2.
Common situations: Old bookmarks/links predating the host~id scheme; callers passing a bare Docker container ID; frontend state that lost the host component; IDs echoed through other tools that split or re-join on different separators.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
AI-assisted analysis of amir20/dozzle@d9463cbe21 (2026-09-07).
Data as JSON: /api/errors/1197839c8b5f27d2.
Report an issue: GitHub.
Appendix: source
Thrown at internal/web/download.go:95
for _, level := range r.URL.Query()["levels"] {
levels[level] = struct{}{}
}
}
// Validate all containers before starting to write response
type containerInfo struct {
hostId string
host string
id string
containerService *container_support.ContainerService
}
containers := make([]containerInfo, 0, len(hostIds))
for _, hostId := range hostIds {
parts := strings.Split(hostId, "~")
if len(parts) != 2 {
log.Error().Msgf("invalid host id: %s", hostId)
http.Error(w, fmt.Sprintf("invalid host id: %s", hostId), http.StatusBadRequest)
return
}
host := parts[0]
id := parts[1]
containerService, err := h.hostService.FindContainer(host, id, userLabels)
if err != nil {
log.Error().Err(err).Msgf("error finding container %s", id)
http.Error(w, fmt.Sprintf("error finding container %s: %v", id, err), http.StatusBadRequest)
return
}
containers = append(containers, containerInfo{
hostId: hostId,
host: host,
id: id,
containerService: containerService,
})View on GitHub (pinned to d9463cbe21)