henrygd/beszel · error

logs request failed: %s: %s

Error message

logs request failed: %s: %s

What it means

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.

Source

Thrown at agent/docker.go:872

	}
	endpoint, err := buildDockerContainerEndpoint(containerID, "logs", query)
	if err != nil {
		return "", err
	}
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return "", err
	}

	resp, err := dm.client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(io.LimitReader(resp.Body, 1024))
		return "", fmt.Errorf("logs request failed: %s: %s", resp.Status, strings.TrimSpace(string(body)))
	}

	var builder strings.Builder
	contentType := resp.Header.Get("Content-Type")
	multiplexed := strings.HasSuffix(contentType, "multiplexed-stream")
	logReader := io.Reader(resp.Body)
	if !multiplexed {
		// Podman may return multiplexed logs without Content-Type. Sniff the first frame header
		// with a small buffered reader only when the header check fails.
		bufferedReader := bufio.NewReaderSize(resp.Body, 8)
		multiplexed = detectDockerMultiplexedStream(bufferedReader)
		logReader = bufferedReader
	}
	if err := decodeDockerLogStream(logReader, &builder, multiplexed); err != nil {
		return "", err
	}

	// Strip ANSI escape sequences from logs for clean display in web UI

View on GitHub (pinned to b38fb7dafa)

Solutions

  1. Check status and body in the message — 404 means the container is gone, 400 means bad params
  2. Ensure stdout=true or stderr=true is set in the log request query
  3. Refresh the container ID via a new list call before retrying
  4. Retry transient 5xx with backoff

Example fix

// before: missing stream flags
q := url.Values{}
logs, err := getLogs(id, q)
// after
q := url.Values{}
q.Set("stdout", "true")
q.Set("stderr", "true")
q.Set("tail", "100")
logs, err := getLogs(id, q)
Defensive patterns

Strategy: validation

Validate before calling

q := url.Values{}
q.Set("stdout", "true")
q.Set("stderr", "true")
q.Set("tail", "200")
// confirm container exists first
if !containerExists(id) { return }

Try / catch

logs, err := getLogs(id, q)
if err != nil {
    var statusErr string
    if strings.Contains(err.Error(), "404") { statusErr = "gone" }
    log.Warn("logs unavailable", "container", id, "reason", statusErr, "err", err)
    return
}

Prevention

When it happens

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

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

Related errors


AI-assisted analysis of henrygd/beszel@b38fb7dafa (2026-08-31). Data as JSON: /api/errors/e3f723da70a6a424. Report an issue: GitHub.