{"record":{"id":"1e98e9f6691d9d5f","repo":"wavetermdev/waveterm","slug":"stream-file-protocol-error-directory-entry-has-da","errorCode":null,"errorMessage":"stream file protocol error, directory entry has data","messagePattern":"stream file protocol error, directory entry has data","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/remote/fileshare/fsutil/fsutil.go","lineNumber":105,"sourceCode":"\t\t\tif firstPk {\n\t\t\t\tfirstPk = false\n\t\t\t\t// first packet has the fileinfo\n\t\t\t\tif resp.Info == nil {\n\t\t\t\t\treturn fmt.Errorf(\"stream file protocol error, first pk fileinfo is empty\")\n\t\t\t\t}\n\t\t\t\tfileData = &resp\n\t\t\t\tif fileData.Info.IsDir {\n\t\t\t\t\tisDir = true\n\t\t\t\t}\n\t\t\t\tfileInfoCallback(*fileData.Info)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif isDir {\n\t\t\t\tif len(resp.Entries) == 0 {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif resp.Data64 != \"\" {\n\t\t\t\t\treturn fmt.Errorf(\"stream file protocol error, directory entry has data\")\n\t\t\t\t}\n\t\t\t\tif err := dirCallback(resp.Entries); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif resp.Data64 == \"\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tdecoder := base64.NewDecoder(base64.StdEncoding, bytes.NewReader([]byte(resp.Data64)))\n\t\t\t\tif err := fileCallback(decoder); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc ReadStreamToFileData(ctx context.Context, readCh <-chan wshrpc.RespOrErrorUnion[wshrpc.FileData]) (*wshrpc.FileData, error) {","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/remote/fileshare/fsutil/fsutil.go#L87-L123","documentation":"When streaming a directory, each packet should contain directory entries and no payload data. ReadFileStream rejects a directory packet that carries Data64, since directory listings must not include file content — a protocol violation by the remote responder.","triggerScenarios":"ReadStreamToFileData on a directory where the remote sends entries together with non-empty Data64 — e.g. a mismatched fileshare implementation or corrupted stream framing.","commonSituations":"Version mismatch between client and remote wsh protocol; remote bug sending file-style packets for directories; stream desync after a prior packet error.","solutions":["Ensure client and remote wsh versions match and are current","Restart the file stream — desynced streams cannot recover mid-stream","List the directory entries individually instead of streaming as one call","Report/inspect the remote responder implementation if reproducible"],"exampleFix":"// before\ndata, err := fsutil.ReadStreamToFileData(ctx, ctl, dirPath) // dir packet has data\n// after\nif isRemoteDir(ctx, ctl, dirPath) {\n    err = streamDirEntriesOnly(ctx, ctl, dirPath) // entries-only streaming path\n} else {\n    data, err = fsutil.ReadStreamToFileData(ctx, ctl, dirPath)\n}","handlingStrategy":"type-guard","validationCode":"fi, _ := remoteStat(ctx, ctl, path)\nif fi != nil && fi.IsDir {\n    // use the directory streaming path, not the file-data path\n}","typeGuard":"func isDirPacketOk(resp fileshare.StreamResponse) bool {\n    return resp.Info != nil && !resp.Info.IsDir || (resp.Info.IsDir && resp.Data64 == \"\")\n}","tryCatchPattern":"_, err := fsutil.ReadStreamToFileData(ctx, ctl, dir)\nif err != nil && strings.Contains(err.Error(), \"directory entry has data\") {\n    return fmt.Errorf(\"remote protocol mismatch — upgrade wsh on both ends\")\n}","preventionTips":["Keep wsh versions identical on client and remote","Use entries-only streaming for directories","Restart streams from scratch after any protocol error"],"tags":["go","protocol","stream"],"backgroundTag":"stream-protocol-violation","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}