wavetermdev/waveterm · error
stream file protocol error, first pk fileinfo is empty
Error message
stream file protocol error, first pk fileinfo is empty
What it means
The fileshare stream protocol requires the first packet from the remote to carry the FileInfo (resp.Info). ReadFileStream enforces this; if the first packet arrives with a nil Info it means the remote violated the protocol, so the stream is aborted with this error.
Source
Thrown at pkg/remote/fileshare/fsutil/fsutil.go:91
for {
select {
case <-ctx.Done():
return fmt.Errorf("context cancelled: %v", context.Cause(ctx))
case respUnion, ok := <-readCh:
if !ok {
drain = false
return nil
}
if respUnion.Error != nil {
return respUnion.Error
}
resp := respUnion.Response
if firstPk {
firstPk = false
// first packet has the fileinfo
if resp.Info == nil {
return fmt.Errorf("stream file protocol error, first pk fileinfo is empty")
}
fileData = &resp
if fileData.Info.IsDir {
isDir = true
}
fileInfoCallback(*fileData.Info)
continue
}
if isDir {
if len(resp.Entries) == 0 {
continue
}
if resp.Data64 != "" {
return fmt.Errorf("stream file protocol error, directory entry has data")
}
if err := dirCallback(resp.Entries); err != nil {
return err
}View on GitHub (pinned to a4447c1563)
Solutions
- Retry the read — a deletion race usually succeeds on a second attempt
- Verify the file still exists on the remote before streaming
- Upgrade wsh on both client and remote to matching versions
- Check remote-side logs for responder errors at stream start
Example fix
// before
data, err := fsutil.ReadStreamToFileData(ctx, ctl, path) // protocol error
// after
if exists, _ := remoteFileExists(ctx, ctl, path); !exists {
return fmt.Errorf("remote file %q gone, skipping", path)
}
data, err := fsutil.ReadStreamToFileData(ctx, ctl, path) Defensive patterns
Strategy: retry
Validate before calling
if ok, _ := remoteExists(ctx, ctl, path); !ok {
return fmt.Errorf("remote path %q no longer exists", path)
} Type guard
func hasFileInfo(resp *fileshare.StreamResponse) bool { return resp != nil && resp.Info != nil } Try / catch
data, err := fsutil.ReadStreamToFileData(ctx, ctl, path)
if err != nil && strings.Contains(err.Error(), "first pk fileinfo is empty") {
time.Sleep(250 * time.Millisecond) // deletion race: retry once
data, err = fsutil.ReadStreamToFileData(ctx, ctl, path)
} Prevention
- Match wsh versions on client and remote
- Avoid deleting/moving files while they are being streamed
- Check existence immediately before streaming in racy workflows
When it happens
Trigger: ReadStreamToFileData streaming a path where the remote responder sends a first packet without file info — the remote file vanished between stat and stream, or the remote side (older/mismatched wsh) does not populate Info.
Common situations: File deleted or renamed on remote right after the read started; version mismatch between client and remote wsh fileshare implementations; race with another writer replacing the file.
Related errors
- stream file protocol error, directory entry has data
- stream file protocol error, no file info
- failed to copy data: %w
- beginning of file
- procinfo: process not found
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/eaa47b68561a6b7e.
Report an issue: GitHub.