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

  1. Inspect the wrapped error: read errors mean the source reader failed — check the producer (upstream request, pipe, file) for logs/health.
  2. If the reader is a response body or pipe, ensure it is fully available and not closed concurrently by another goroutine.
  3. Treat write errors (EPIPE/ECONNRESET) as client cancellation and skip retrying.
  4. Buffer small/known-size content into Data instead of a live Reader when reliability matters more than memory.
  5. 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

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


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/003a1a2907bb15ed. Report an issue: GitHub.