ipfs/kubo · error

e (stream error trailer header value)

Error message

e (stream error trailer header value)

What it means

trailerReader wraps the HTTP response body of streaming RPC calls; on a body read error it checks the X-Stream-Error trailer and, if present, replaces the underlying io error with the daemon's error string. Callers of any Exec/Decode that streams (e.g. ls, log tail, pin ls stream) see the server's error as the Read error.

Source

Thrown at client/rpc/response.go:28

	"net/url"
	"os"

	"github.com/ipfs/boxo/files"
	cmds "github.com/ipfs/go-ipfs-cmds"
	cmdhttp "github.com/ipfs/go-ipfs-cmds/http"
)

type Error = cmds.Error

type trailerReader struct {
	resp *http.Response
}

func (r *trailerReader) Read(b []byte) (int, error) {
	n, err := r.resp.Body.Read(b)
	if err != nil {
		if e := r.resp.Trailer.Get(cmdhttp.StreamErrHeader); e != "" {
			err = errors.New(e)
		}
	}
	return n, err
}

func (r *trailerReader) Close() error {
	return r.resp.Body.Close()
}

type Response struct {
	Output io.ReadCloser
	Error  *Error
}

func (r *Response) Close() error {
	if r.Output != nil {

		// drain output (response body)

View on GitHub (pinned to 329838acdf)

Solutions

  1. Treat the message as the daemon's failure reason and fix the server-side cause; do not retry assuming a plain network error.
  2. Retry the whole request if the message indicates a transient daemon condition (canceled context, temporary resource exhaustion).
  3. Check daemon logs at the matching timestamp for the underlying stack trace.
  4. Use the request's context to enforce your own client-side timeout so long streams fail predictably.

Example fix

// before
resp, _ := api.Request("pin/ls").Send(ctx)
io.Copy(buf, resp.Output) // opaque error from trailer
// after
resp, _ := api.Request("pin/ls").Send(ctx)
if _, err := io.Copy(buf, resp.Output); err != nil {
    return fmt.Errorf("pin/ls stream failed: %w", err) // message is the daemon's trailer error
}
Defensive patterns

Strategy: try-catch

Try / catch

resp, err := api.Request("pin/ls").Send(ctx)
if err != nil { return err }
_, err = io.Copy(buf, resp.Output)
if err != nil {
    // err text is the daemon's X-Stream-Error trailer value
    return fmt.Errorf("rpc stream failed: %w", err)
}

Prevention

When it happens

Trigger: Any streaming RPC response (io.Copy on api.Request(...).Send, reading log tail or ls output) where the daemon encounters an error mid-stream and sets the StreamErrHeader trailer while closing the body — surfacing io.ReadAll/io.Copy as this error instead of io.ErrUnexpectedEOF.

Common situations: Daemon-side failures during long listings (context canceled on the server, internal errors), connections interrupted mid-response where the trailer still arrived, or reading a streamed response to completion after the daemon aborted the request.

Related errors


AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03). Data as JSON: /api/errors/fb1c112d2633656d. Report an issue: GitHub.