{"record":{"id":"d5509560cdfeae85","repo":"MemPalace/mempalace","slug":"invalid-content-length","errorCode":null,"errorMessage":"invalid Content-Length","messagePattern":"invalid Content-Length","errorType":"http","errorClass":"ValueError","httpStatus":400,"severity":"warning","filePath":"mempalace/daemon.py","lineNumber":936,"sourceCode":"        timeout = 10\n\n        def log_message(self, fmt, *args):  # pragma: no cover - stdlib access logging noise\n            return\n\n        def _authorized(self) -> bool:\n            auth = self.headers.get(\"Authorization\")\n            if auth and secrets.compare_digest(auth, f\"Bearer {token}\"):\n                return True\n            _json_response(self, 401, {\"error\": \"unauthorized\"})\n            return False\n\n        def _read_json(self) -> dict[str, Any]:\n            length = int(self.headers.get(\"Content-Length\", \"0\") or \"0\")\n            # Reject a negative Content-Length explicitly: self.rfile.read(-1)\n            # would read until the client closes the connection, blocking the\n            # worker and bypassing the MAX_BODY_BYTES cap (an auth-gated DoS).\n            if length < 0:\n                raise ValueError(\"invalid Content-Length\")\n            if length > MAX_BODY_BYTES:\n                raise ValueError(\"request body too large\")\n            raw = self.rfile.read(length)\n            return json.loads(raw.decode(\"utf-8\")) if raw else {}\n\n        def do_GET(self):\n            if not self._authorized():\n                return\n            try:\n                self._handle_get()\n            except Exception as exc:  # noqa: BLE001 - malformed query/DB error → 400\n                _json_response(self, 400, {\"error\": str(exc)})\n\n        def _handle_get(self):\n            parsed = urlparse(self.path)\n            if parsed.path == \"/health\":\n                _json_response(\n                    self,","sourceCodeStart":918,"sourceCodeEnd":954,"githubUrl":"https://github.com/MemPalace/mempalace/blob/06cb6987f02610784fefbad4b2bd5d026d164ba6/mempalace/daemon.py#L918-L954","documentation":"ValueError('invalid Content-Length') raised in the daemon's HTTP request handler (_read_json, mempalace/daemon.py:936) when the Content-Length header is negative. A negative length would make self.rfile.read(-1) read until the client closes the connection, blocking a worker thread and bypassing the MAX_BODY_BYTES cap — so it is rejected explicitly as a hardening measure. This error is produced inside the daemon server; a client that sends the malformed header receives a 400 response carrying this message.","triggerScenarios":"A hand-rolled HTTP client sending 'Content-Length: -1'; a corrupted/truncated request replayed by a proxy or test harness; negative Content-Length crafted by an authenticated caller (it is behind bearer auth, so it is an auth-gated DoS being prevented, not an anonymous one).","commonSituations":"Testing the daemon with custom sockets or curl with miscomputed lengths; buggy middleware that rewrites headers; security scanners probing the local endpoint after reading the token.","solutions":["If you are the client author: send a valid non-negative Content-Length (or omit the body and use GET).","If you see this in daemon logs: identify the client sending malformed headers; it already has the bearer token, so treat it as a buggy or compromised client and investigate.","Use urllib/requests-style clients that compute Content-Length automatically instead of raw sockets."],"exampleFix":"# before (raw socket)\nsock.sendall(b\"POST /jobs HTTP/1.1\\r\\nContent-Length: -1\\r\\n\\r\\n\")\n\n# after\nimport json, urllib.request\nreq = urllib.request.Request(url, data=json.dumps(payload).encode(), method=\"POST\")","handlingStrategy":"validation","validationCode":"# client-side: never send a negative Content-Length\nbody = json.dumps(payload).encode()\nassert len(body) >= 0  # any real serializer produces this; raw sockets don't","typeGuard":null,"tryCatchPattern":"# server-side: the handler already converts this to a 400 JSON response\ntry:\n    data = self._read_json()\nexcept ValueError as exc:\n    _json_response(self, 400, {\"error\": str(exc)})","preventionTips":["Use standard HTTP libraries (urllib, requests) that compute Content-Length for you.","Never hand-craft Content-Length headers in tests or proxies.","Monitor daemon 400s — repeated malformed headers from an authenticated client indicate a bug or compromise."],"tags":["daemon","http","security","hardening"],"backgroundTag":null,"analyzedSha":"06cb6987f02610784fefbad4b2bd5d026d164ba6","analyzedAt":"2026-08-15T03:03:36.213Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}