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
- Treat the message as the daemon's failure reason and fix the server-side cause; do not retry assuming a plain network error.
- Retry the whole request if the message indicates a transient daemon condition (canceled context, temporary resource exhaustion).
- Check daemon logs at the matching timestamp for the underlying stack trace.
- 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
- Set client-side context timeouts on all streaming RPC calls
- Check daemon logs when a stream error message appears
- Distinguish trailer errors from io.EOF/io.ErrUnexpectedEOF before retry logic
- Keep daemon and client updated; stream error formats can evolve
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
- unknowm mhType %d
- cids didn't match - local %s, remote %s
- msg (server-provided error message)
- got unexpected number of keys back
- Name.Resolve: depth other than 1 or %d not supported
AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03).
Data as JSON: /api/errors/fb1c112d2633656d.
Report an issue: GitHub.