wavetermdev/waveterm · error
failed to copy from reader: %v
Error message
failed to copy from reader: %v
What it means
After writing the MIME-detected buffered prefix, ServeFileOption streams the remainder of an option.Reader io.Reader to the ResponseWriter with io.Copy. This error wraps any failure reading from the underlying reader or writing to the client during that copy. It means the response body was truncated and the client received an incomplete response.
Source
Thrown at pkg/waveapp/waveapp.go:441
case option.File != nil:
if bufferedData != nil {
if _, err := w.Write(bufferedData); err != nil {
return fmt.Errorf("failed to write buffered data: %v", err)
}
}
if _, err := io.Copy(w, option.File); err != nil {
return fmt.Errorf("failed to copy from file: %v", err)
}
case option.Reader != nil:
if bufferedData != nil {
if _, err := w.Write(bufferedData); err != nil {
return fmt.Errorf("failed to write buffered data: %v", err)
}
}
if _, err := io.Copy(w, option.Reader); err != nil {
return fmt.Errorf("failed to copy from reader: %v", err)
}
default:
return fmt.Errorf("no content available")
}
return nil
}
func (c *Client) RegisterFilePrefixHandler(prefix string, optionProvider func(path string) (*FileHandlerOption, error)) {
c.UrlHandlerMux.PathPrefix(prefix).HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
option, err := optionProvider(r.URL.Path)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if option == nil {
http.Error(w, "no content available", http.StatusNotFound)View on GitHub (pinned to a4447c1563)
Solutions
- Inspect the wrapped error: read errors mean the source reader failed — check the producer (upstream request, pipe, file) for logs/health.
- If the reader is a response body or pipe, ensure it is fully available and not closed concurrently by another goroutine.
- Treat write errors (EPIPE/ECONNRESET) as client cancellation and skip retrying.
- Buffer small/known-size content into Data instead of a live Reader when reliability matters more than memory.
- Add error logging including the stream's origin so truncated responses can be traced.
Example fix
// before
resp, _ := http.Get(upstreamURL)
defer resp.Body.Close()
err := waveapp.ServeFileOption(w, r, waveapp.FileHandlerOption{Reader: resp.Body}) // upstream drop -> truncated response
// after
resp, _ := http.Get(upstreamURL)
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
http.Error(w, "upstream unavailable", http.StatusBadGateway)
return
}
err := waveapp.ServeFileOption(w, r, waveapp.FileHandlerOption{Reader: resp.Body}) Defensive patterns
Strategy: try-catch
Validate before calling
if option.Reader == nil {
return fmt.Errorf("option.Reader must be set")
}
// For known-size sources, verify readability up front
if f, ok := option.Reader.(*os.File); ok {
if _, err := f.Stat(); err != nil {
return fmt.Errorf("reader source unreadable: %v", err)
}
} Type guard
func isHealthyReader(r io.Reader) bool {
if f, ok := r.(*os.File); ok {
_, err := f.Stat()
return err == nil
}
return r != nil
} Try / catch
err := waveapp.ServeFileOption(w, r, opt)
if err != nil {
select {
case <-r.Context().Done():
return // client canceled; ignore
default:
}
log.Printf("stream copy failed: %v", err)
// headers already sent; cannot change status — just abort
} Prevention
- Verify upstream sources (HTTP bodies, pipes) are alive and not closed concurrently.
- For critical small payloads, load into option.Data instead of streaming a live Reader.
- Include the stream origin in logs so truncated responses are traceable.
- Remember the status code is already sent; a mid-copy failure cannot become a 500.
When it happens
Trigger: Calling ServeFileOption with FileHandlerOption{Reader: r} when r returns an error mid-stream: a closed pipe, a network/HTTP body read error, an os.File read failure, or a failed write to a disconnected client.
Common situations: Streaming content from an upstream HTTP response whose connection drops; reading from a process pipe (exec output) that exits early; serving from a network file; client disconnects partway through the transfer.
Related errors
- failed to copy from file: %v
- wcloud endpoint not set
- wcloud ping endpoint not set
- reading file: %w
- no pending stream (call PrepareConnect first)
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/003a1a2907bb15ed.
Report an issue: GitHub.