{"record":{"id":"e3f723da70a6a424","repo":"henrygd/beszel","slug":"logs-request-failed-s-s","errorCode":null,"errorMessage":"logs request failed: %s: %s","messagePattern":"logs request failed: (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/docker.go","lineNumber":872,"sourceCode":"\t}\n\tendpoint, err := buildDockerContainerEndpoint(containerID, \"logs\", query)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tresp, err := dm.client.Do(req)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tbody, _ := io.ReadAll(io.LimitReader(resp.Body, 1024))\n\t\treturn \"\", fmt.Errorf(\"logs request failed: %s: %s\", resp.Status, strings.TrimSpace(string(body)))\n\t}\n\n\tvar builder strings.Builder\n\tcontentType := resp.Header.Get(\"Content-Type\")\n\tmultiplexed := strings.HasSuffix(contentType, \"multiplexed-stream\")\n\tlogReader := io.Reader(resp.Body)\n\tif !multiplexed {\n\t\t// Podman may return multiplexed logs without Content-Type. Sniff the first frame header\n\t\t// with a small buffered reader only when the header check fails.\n\t\tbufferedReader := bufio.NewReaderSize(resp.Body, 8)\n\t\tmultiplexed = detectDockerMultiplexedStream(bufferedReader)\n\t\tlogReader = bufferedReader\n\t}\n\tif err := decodeDockerLogStream(logReader, &builder, multiplexed); err != nil {\n\t\treturn \"\", err\n\t}\n\n\t// Strip ANSI escape sequences from logs for clean display in web UI","sourceCodeStart":854,"sourceCodeEnd":890,"githubUrl":"https://github.com/henrygd/beszel/blob/b38fb7dafa60812cc22e6a84ce313e94f1ce0a32/agent/docker.go#L854-L890","documentation":"getLogs requests container logs from the Docker API and streams the response. This error is thrown when the logs endpoint returns a non-200 status; the message carries the HTTP status plus up to 1KB of the daemon's response body. It means no log stream was obtained at all, as opposed to decode/stream errors later in the function.","triggerScenarios":"GET /containers/{id}/logs with query params (tail, stdout, stderr, timestamps) returns non-200: 404 (container missing), 400 (bad query params, e.g. neither stdout nor stderr requested), 403, or 5xx.","commonSituations":"Requesting logs for a container that just exited/removed; malformed tail/since parameter values; API-proxy stripping required query parameters; daemon restarted between listing and log fetch.","solutions":["Check status and body in the message — 404 means the container is gone, 400 means bad params","Ensure stdout=true or stderr=true is set in the log request query","Refresh the container ID via a new list call before retrying","Retry transient 5xx with backoff"],"exampleFix":"// before: missing stream flags\nq := url.Values{}\nlogs, err := getLogs(id, q)\n// after\nq := url.Values{}\nq.Set(\"stdout\", \"true\")\nq.Set(\"stderr\", \"true\")\nq.Set(\"tail\", \"100\")\nlogs, err := getLogs(id, q)","handlingStrategy":"validation","validationCode":"q := url.Values{}\nq.Set(\"stdout\", \"true\")\nq.Set(\"stderr\", \"true\")\nq.Set(\"tail\", \"200\")\n// confirm container exists first\nif !containerExists(id) { return }","typeGuard":null,"tryCatchPattern":"logs, err := getLogs(id, q)\nif err != nil {\n    var statusErr string\n    if strings.Contains(err.Error(), \"404\") { statusErr = \"gone\" }\n    log.Warn(\"logs unavailable\", \"container\", id, \"reason\", statusErr, \"err\", err)\n    return\n}","preventionTips":["Always request stdout and/or stderr in log queries","Validate tail/since parameter formats before sending","Re-list containers after daemon restarts; handle 404 gracefully"],"tags":["http","docker-logs","stream"],"backgroundTag":"http-non-200-response","analyzedSha":"b38fb7dafa60812cc22e6a84ce313e94f1ce0a32","analyzedAt":"2026-08-31T15:10:10.149Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}