{"record":{"id":"51bf05ffd8a27b15","repo":"amir20/dozzle","slug":"err-error-51bf05","errorCode":null,"errorMessage":"err.Error()","messagePattern":"err\\.Error\\(\\)","errorType":"http","errorClass":null,"httpStatus":404,"severity":"error","filePath":"internal/web/imagecheck.go","lineNumber":22,"sourceCode":"\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/amir20/dozzle/internal/imagecheck\"\n\t\"github.com/go-chi/chi/v5\"\n\t\"github.com/rs/zerolog/log\"\n)\n\n// checkImageUpdate reports whether a newer image exists upstream for a\n// container. It is deliberately not gated behind EnableActions: knowing a\n// container is out of date is useful even when the user updates it themselves\n// through compose. Only the update button depends on actions being enabled.\nfunc (h *handler) checkImageUpdate(w http.ResponseWriter, r *http.Request) {\n\tid := chi.URLParam(r, \"id\")\n\n\tcontainerService, err := h.hostService.FindContainer(hostKey(r), id, h.resolveLabels(r))\n\tif err != nil {\n\t\tlog.Error().Err(err).Msg(\"error while trying to find container\")\n\t\thttp.Error(w, err.Error(), http.StatusNotFound)\n\t\treturn\n\t}\n\n\t// A forced check bypasses the digest cache and is what the explicit\n\t// \"check now\" affordance sends.\n\tforce := r.URL.Query().Get(\"force\") == \"true\"\n\n\t// In manual mode Dozzle never reaches a registry on its own. Background\n\t// checks are answered without egress so the frontend can stay uniform.\n\tif h.config.ImageCheckMode == imagecheck.ModeManual && !force {\n\t\twriteJSON(w, http.StatusOK, imagecheck.Result{\n\t\t\tImage:     containerService.Container.Image,\n\t\t\tStatus:    imagecheck.StatusSkipped,\n\t\t\tReason:    \"image checks are set to manual\",\n\t\t\tCheckedAt: time.Now(),\n\t\t})\n\t\treturn\n\t}","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/amir20/dozzle/blob/d9463cbe21874e44ab79db6fa63e746ca7d22928/internal/web/imagecheck.go#L4-L40","documentation":"checkImageUpdate returns a 404 with err.Error() when hostService.FindContainer cannot resolve the container id from hostKey(r). This mirrors the lookup failure family: bad host, unknown id, or user-label filtering excluding the container.","triggerScenarios":"GET /api/hosts/{host}/containers/{id}/check-update (or equivalent route) with a stale container id, wrong host, or a container filtered out by userLabels.","commonSituations":"Container recreated after upgrade (id changed); host removed from the multi-host config; checking update for a container visible under a different host than the one in the URL.","solutions":["Re-fetch the container list and use the fresh container id.","Confirm the host segment in the URL matches the container's actual host.","Verify userLabels configuration is not excluding the container from lookups."],"exampleFix":"// before\nawait fetch(`/api/hosts/${savedHost}~${savedId}/check-update`)\n// after\nconst c = await findContainerByName(name)\nawait fetch(`/api/hosts/${c.host}~${c.id}/check-update`)","handlingStrategy":"validation","validationCode":"const containers = await fetch('/api/containers.json').then(r => r.json());\nconst exists = containers.some(c => c.host === host && c.id === id);\nif (!exists) throw new Error(`container ${host}~${id} not found`);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Re-resolve container ids from the live list before calling per-container routes.","Handle 404 by refreshing the container list and retrying once.","Track containers by name/label, not by persisted id."],"tags":["http","docker","resource-not-found"],"backgroundTag":"record-not-found","analyzedSha":"d9463cbe21874e44ab79db6fa63e746ca7d22928","analyzedAt":"2026-09-07T10:08:55.855Z","contentChangedAt":"2026-09-07T10:08:55.855Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}