{"record":{"id":"d7373d8750521aeb","repo":"louislam/uptime-kuma","slug":"docker-host-not-found","errorCode":null,"errorMessage":"docker host not found","messagePattern":"docker host not found","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/docker.js","lineNumber":28,"sourceCode":"    static CertificateFileNameCA = \"ca.pem\";\n    static CertificateFileNameCert = \"cert.pem\";\n    static CertificateFileNameKey = \"key.pem\";\n\n    /**\n     * Save a docker host\n     * @param {object} dockerHost Docker host to save\n     * @param {?number} dockerHostID ID of the docker host to update\n     * @param {number} userID ID of the user who adds the docker host\n     * @returns {Promise<Bean>} Updated docker host\n     */\n    static async save(dockerHost, dockerHostID, userID) {\n        let bean;\n\n        if (dockerHostID) {\n            bean = await R.findOne(\"docker_host\", \" id = ? AND user_id = ? \", [dockerHostID, userID]);\n\n            if (!bean) {\n                throw new Error(\"docker host not found\");\n            }\n        } else {\n            bean = R.dispense(\"docker_host\");\n        }\n\n        bean.user_id = userID;\n        bean.docker_daemon = dockerHost.dockerDaemon;\n        bean.docker_type = dockerHost.dockerType;\n        bean.name = dockerHost.name;\n\n        await R.store(bean);\n\n        return bean;\n    }\n\n    /**\n     * Delete a Docker host\n     * @param {number} dockerHostID ID of the Docker host to delete","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/louislam/uptime-kuma/blob/6b5ea0155793e666666745fb8d6fef1e829543a2/server/docker.js#L10-L46","documentation":"DockerHost.save() with a non-null dockerHostID performs an ownership-scoped lookup: `findOne(\"docker_host\", \" id = ? AND user_id = ? \", [dockerHostID, userID])`. A miss means either the docker host does not exist or it belongs to a different user. Both branches are reported the same way on purpose — leaking 'exists but not yours' would be an info disclosure.","triggerScenarios":"Calling the PUT /api/docker-hosts/{id} endpoint (which routes to save) with an id that was deleted, never existed, or was created by another user account. Also hit if the userID passed in does not match the session user due to a session/auth bug.","commonSituations":"Multi-user instances where one user tries to edit another's docker host; stale frontend state after another user deleted the host; a URL/ID typo in a direct API call.","solutions":["Refresh the docker-hosts list in the UI and retry against a current id.","Confirm the authenticated user is the owner of that docker host; if not, have the owner perform the edit or re-create the host under your account.","If the row was deleted, create a new docker host instead of updating the old id.","Check the server logs for the preceding query — the id/userID pair will show exactly why no row matched."],"exampleFix":"// before — update without existence check\nconst bean = await R.findOne(\"docker_host\", \" id = ? AND user_id = ? \", [dockerHostID, userID]);\nif (!bean) {\n    throw new Error(\"docker host not found\");\n}\n\n// after — return a 404-shaped error so the API layer can map it\nif (!bean) {\n    const err = new Error(\"docker host not found\");\n    err.status = 404;\n    throw err;\n}","handlingStrategy":"validation","validationCode":"// Verify ownership before calling DockerHost.save(..., dockerHostID, userID)\nconst { R } = require(\"redbean-node\");\nasync function userOwnsDockerHost(dockerHostID, userID) {\n    const row = await R.findOne(\"docker_host\", \" id = ? AND user_id = ? \", [dockerHostID, userID]);\n    return row !== null;\n}","typeGuard":"function isOwnedByUser(row, userID) {\n    return !!row && Number(row.user_id) === Number(userID);\n}","tryCatchPattern":"try {\n    await DockerHost.save(dockerHost, dockerHostID, userID);\n} catch (e) {\n    if (/docker host not found/.test(e.message)) {\n        // return 404 to the client; refresh the list in the UI\n    }\n    throw e;\n}","preventionTips":["Cache the docker-host list per user and avoid showing ids the user cannot own.","Issue updates by id only after a fresh fetch, not from long-lived UI state.","Treat 'not found' and 'not owned' identically on the wire to avoid info leakage."],"tags":["docker","authorization","api","not-found"],"backgroundTag":null,"analyzedSha":"6b5ea0155793e666666745fb8d6fef1e829543a2","analyzedAt":"2026-08-12T23:42:12.959Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}