{"record":{"id":"b27c683690e77e15","repo":"louislam/uptime-kuma","slug":"invalid-docker-response-is-it-docker-really-a-dae","errorCode":null,"errorMessage":"Invalid Docker response, is it Docker really a daemon?","messagePattern":"Invalid Docker response, is it Docker really a daemon\\?","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"server/docker.js","lineNumber":95,"sourceCode":"\n        if (dockerHost.dockerType === \"socket\") {\n            options.socketPath = dockerHost.dockerDaemon;\n        } else if (dockerHost.dockerType === \"tcp\") {\n            options.baseURL = DockerHost.patchDockerURL(dockerHost.dockerDaemon);\n            options.httpsAgent = new https.Agent(\n                await DockerHost.getHttpsAgentOptions(dockerHost.dockerType, options.baseURL)\n            );\n        }\n\n        try {\n            let res = await axios.request(options);\n\n            if (Array.isArray(res.data)) {\n                if (res.data.length > 1) {\n                    if (\"ImageID\" in res.data[0]) {\n                        return res.data.length;\n                    } else {\n                        throw new Error(\"Invalid Docker response, is it Docker really a daemon?\");\n                    }\n                } else {\n                    return res.data.length;\n                }\n            } else {\n                throw new Error(\"Invalid Docker response, is it Docker really a daemon?\");\n            }\n        } catch (e) {\n            if (e.code === \"ECONNABORTED\" || e.name === \"CanceledError\") {\n                throw new Error(\"Connection to Docker daemon timed out.\");\n            } else {\n                throw e;\n            }\n        }\n    }\n\n    /**\n     * Since axios 0.27.X, it does not accept `tcp://` protocol.","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/louislam/uptime-kuma/blob/6b5ea0155793e666666745fb8d6fef1e829543a2/server/docker.js#L77-L113","documentation":"In testDockerHost(), after a successful axios.request to the daemon, the response body must be an array. When the array has more than one element, the first element must contain an `ImageID` key — that is the field the real Docker /containers/json endpoint returns per container. If it is missing, the endpoint answered with something that is array-shaped but not the Docker containers API (a reverse proxy error page split into chunks, a JSON array from a different service, etc.).","triggerScenarios":"Pointing a docker host at a URL/path that returns an array without ImageID: a registry API, a load balancer returning an array of backend info, a TCP host that is actually a different JSON service, or a proxy in front of the daemon rewriting responses.","commonSituations":"Misconfigured socketPath/baseURL (e.g. pointed at /v1.40/info by mistake); a TCP load balancer that round-robins to a non-docker service; an authenticated proxy returning a JSON error array.","solutions":["Verify the daemon URL with `curl --unix-socket /var/run/docker.sock http://localhost/containers/json?all=true` (socket) or `curl https://host:2376/containers/json?all=true` (TCP) — the first element must include ImageID.","Confirm you are hitting the Docker Engine API and not a registry or a different JSON service on the same port.","Remove any reverse-proxy rewrite that alters the /containers/json response body.","If using TLS, check the certificate path (data/docker-tls/<host>/) so you actually reach the daemon rather than a TLS-terminating proxy serving other content."],"exampleFix":"// before\nif (res.data.length > 1) {\n    if (\"ImageID\" in res.data[0]) {\n        return res.data.length;\n    } else {\n        throw new Error(\"Invalid Docker response, is it Docker really a daemon?\");\n    }\n}\n\n// after — include a sample of what came back to aid debugging\nthrow new Error(`Invalid Docker response (no ImageID on item[0]); first item keys: ${Object.keys(res.data[0]).join(\",\")}`);","handlingStrategy":"validation","validationCode":"// Probe the endpoint shape yourself before relying on testDockerHost()\nasync function looksLikeDockerApi(baseURL, socketPath) {\n    const axios = require(\"axios\");\n    const res = await axios.request({ url: \"/containers/json?all=true\", ...(socketPath ? { socketPath } : { baseURL }), timeout: 5000 });\n    return Array.isArray(res.data) && (res.data.length === 0 || \"ImageID\" in res.data[0]);\n}","typeGuard":"function isDockerContainerArray(data) {\n    return Array.isArray(data) && (data.length === 0 || data.every((c) => typeof c === \"object\" && c !== null && \"ImageID\" in c));\n}","tryCatchPattern":"try {\n    const count = await DockerHost.testDockerHost(dockerHost);\n} catch (e) {\n    if (/Invalid Docker response/.test(e.message)) {\n        // the URL is reachable but is NOT the Docker API; reconfigure the daemon path\n    }\n    throw e;\n}","preventionTips":["Smoke-test the daemon with curl before configuring it in the UI.","Always hit /containers/json on the Docker Engine API, not the registry or a proxy root.","Document the expected curl response so operators can self-verify."],"tags":["docker","network","validation"],"backgroundTag":null,"analyzedSha":"6b5ea0155793e666666745fb8d6fef1e829543a2","analyzedAt":"2026-08-12T23:42:12.959Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}