{"record":{"id":"9940c37d9d34d1b2","repo":"wavetermdev/waveterm","slug":"writerchan-checksum-mismatch","errorCode":null,"errorMessage":"WriterChan: checksum mismatch","messagePattern":"WriterChan: checksum mismatch","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/util/iochan/iochan.go","lineNumber":91,"sourceCode":"\t\t\tcase <-ctx.Done():\n\t\t\t\treturn\n\t\t\tcase resp, ok := <-ch:\n\t\t\t\tif !ok {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif resp.Error != nil {\n\t\t\t\t\tcancel(resp.Error)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif _, err := sha256Hash.Write(resp.Response.Data); err != nil {\n\t\t\t\t\tcancel(fmt.Errorf(\"WriterChan: error writing to sha256 hash: %v\", err))\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t// The checksum is sent as the last packet\n\t\t\t\tif resp.Response.Checksum != nil {\n\t\t\t\t\tlocalChecksum := sha256Hash.Sum(nil)\n\t\t\t\t\tif !bytes.Equal(localChecksum, resp.Response.Checksum) {\n\t\t\t\t\t\tcancel(fmt.Errorf(\"WriterChan: checksum mismatch\"))\n\t\t\t\t\t}\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif _, err := w.Write(resp.Response.Data); err != nil {\n\t\t\t\t\tcancel(fmt.Errorf(\"WriterChan: write error: %v\", err))\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}()\n}\n","sourceCodeStart":73,"sourceCodeEnd":103,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/util/iochan/iochan.go#L73-L103","documentation":"WriterChan streams packets received on a channel into an io.Writer while maintaining a running SHA-256 hash of the data. The sender transmits a final packet containing its checksum; when the receiver sees it, it compares that checksum against its locally computed hash. If they differ, the stream was corrupted, truncated, or reordered in transit, so the context is cancelled with this error.","triggerScenarios":"A packet's Data bytes were dropped, duplicated, or altered between ReaderChan (sender) and WriterChan (receiver) — e.g. an RPC layer dropped/duplicated channel packets, two different streams were interleaved into the same channel, or a sender bug wrote data outside of ReaderChan so the checksums were computed over different byte sequences.","commonSituations":"File transfer over wsh RPC failing mid-download with corrupted output; intermediate middleware that re-orders or drops packets from the channel; the writer receiving packets from more than one producer; version mismatch where sender and receiver chunk/hash differently.","solutions":["Compare against a fresh local copy of the source file/data to see where the streams diverged.","Verify only one ReaderChan producer is feeding this WriterChan channel.","Re-run the transfer; intermittent corruption may be a transient transport issue.","Upgrade Wave Term on both ends to ensure matching ReaderChan/WriterChan checksum logic.","Add logging of packet count and byte totals on both sides to pinpoint loss."],"exampleFix":"// before: blind retry or ignoring the error\nch := iochan.ReaderChan(ctx, file, 65536, nil)\niochan.WriterChan(ctx, w, ch, nil, cancel)\n// after: verify source integrity and retransfer once on mismatch\nif isContextErrFrom(ctx, context.Cause(ctx), \"checksum mismatch\") {\n    log.Println(\"transfer corrupted, retransmitting\")\n    seekAndRetransfer(ctx, w, file, 0)\n}","handlingStrategy":"validation","validationCode":"func validateTransfer(src io.Reader, packetCh <-chan wshrpc.RespOrErrorUnion[iochantypes.Packet]) error {\n    h := sha256.New()\n    buf := make([]byte, 65536)\n    for {\n        n, err := src.Read(buf)\n        h.Write(buf[:n])\n        if err == io.EOF { break }\n        if err != nil { return err }\n    }\n    for p := range packetCh {\n        if p.Error != nil { return p.Error }\n        if p.Response.Checksum != nil {\n            if !bytes.Equal(h.Sum(nil), p.Response.Checksum) {\n                return errors.New(\"pre-transfer checksum mismatch\")\n            }\n            return nil\n        }\n    }\n    return nil\n}","typeGuard":"func isChecksumMismatchErr(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"WriterChan: checksum mismatch\")\n}","tryCatchPattern":"if err := context.Cause(ctx); err != nil {\n    if isChecksumMismatchErr(err) {\n        log.Printf(\"transfer corrupted, discarding output and retrying: %v\", err)\n        // re-run the transfer from a known-good source\n    } else {\n        return err\n    }\n}","preventionTips":["Feed WriterChan from exactly one ReaderChan producer per channel.","Verify transferred files against a known-good hash (sha256sum) after large transfers.","Keep sender and receiver builds in sync so chunking/checksum logic matches.","Never mutate packet Data in transit; pass channel packets through unchanged."],"tags":["checksum","data-corruption","streaming","go"],"backgroundTag":"checksum-mismatch","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}