louislam/uptime-kuma · warning · Error

Invalid Docker response, is it Docker really a daemon?

Error message

Invalid Docker response, is it Docker really a daemon?

What it means

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.).

Source

Thrown at server/docker.js:95

        if (dockerHost.dockerType === "socket") {
            options.socketPath = dockerHost.dockerDaemon;
        } else if (dockerHost.dockerType === "tcp") {
            options.baseURL = DockerHost.patchDockerURL(dockerHost.dockerDaemon);
            options.httpsAgent = new https.Agent(
                await DockerHost.getHttpsAgentOptions(dockerHost.dockerType, options.baseURL)
            );
        }

        try {
            let res = await axios.request(options);

            if (Array.isArray(res.data)) {
                if (res.data.length > 1) {
                    if ("ImageID" in res.data[0]) {
                        return res.data.length;
                    } else {
                        throw new Error("Invalid Docker response, is it Docker really a daemon?");
                    }
                } else {
                    return res.data.length;
                }
            } else {
                throw new Error("Invalid Docker response, is it Docker really a daemon?");
            }
        } catch (e) {
            if (e.code === "ECONNABORTED" || e.name === "CanceledError") {
                throw new Error("Connection to Docker daemon timed out.");
            } else {
                throw e;
            }
        }
    }

    /**
     * Since axios 0.27.X, it does not accept `tcp://` protocol.

View on GitHub (pinned to 6b5ea01557)

Solutions

  1. 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.
  2. Confirm you are hitting the Docker Engine API and not a registry or a different JSON service on the same port.
  3. Remove any reverse-proxy rewrite that alters the /containers/json response body.
  4. 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.

Example fix

// before
if (res.data.length > 1) {
    if ("ImageID" in res.data[0]) {
        return res.data.length;
    } else {
        throw new Error("Invalid Docker response, is it Docker really a daemon?");
    }
}

// after — include a sample of what came back to aid debugging
throw new Error(`Invalid Docker response (no ImageID on item[0]); first item keys: ${Object.keys(res.data[0]).join(",")}`);
Defensive patterns

Strategy: validation

Validate before calling

// Probe the endpoint shape yourself before relying on testDockerHost()
async function looksLikeDockerApi(baseURL, socketPath) {
    const axios = require("axios");
    const res = await axios.request({ url: "/containers/json?all=true", ...(socketPath ? { socketPath } : { baseURL }), timeout: 5000 });
    return Array.isArray(res.data) && (res.data.length === 0 || "ImageID" in res.data[0]);
}

Type guard

function isDockerContainerArray(data) {
    return Array.isArray(data) && (data.length === 0 || data.every((c) => typeof c === "object" && c !== null && "ImageID" in c));
}

Try / catch

try {
    const count = await DockerHost.testDockerHost(dockerHost);
} catch (e) {
    if (/Invalid Docker response/.test(e.message)) {
        // the URL is reachable but is NOT the Docker API; reconfigure the daemon path
    }
    throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of louislam/uptime-kuma@6b5ea01557 (2026-08-12). Data as JSON: /api/errors/b27c683690e77e15. Report an issue: GitHub.