{"record":{"id":"9e32b9db114f934a","repo":"kovidgoyal/kitty","slug":"no-header-was-found-in-the-signature-data","errorCode":null,"errorMessage":"No header was found in the signature data","messagePattern":"No header was found in the signature data","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tools/rsync/api.go","lineNumber":128,"sourceCode":"\nfunc (self *Api) read_signature_blocks(data []byte) (consumed int) {\n\tblock_hash_size := self.rsync.HashSize() + 12\n\tfor ; len(data) >= block_hash_size; data = data[block_hash_size:] {\n\t\tbl := BlockHash{}\n\t\tbl.Unserialize(data[:block_hash_size])\n\t\tself.signature = append(self.signature, bl)\n\t\tconsumed += block_hash_size\n\t}\n\treturn\n}\n\nfunc (self *Differ) FinishSignatureData() (err error) {\n\tif len(self.unconsumed_signature_data) > 0 {\n\t\treturn fmt.Errorf(\"There were %d leftover bytes in the signature data\", len(self.unconsumed_signature_data))\n\t}\n\tself.unconsumed_signature_data = nil\n\tif !self.rsync.HasHasher() {\n\t\treturn fmt.Errorf(\"No header was found in the signature data\")\n\t}\n\treturn\n}\n\nfunc (self *Patcher) update_delta(data []byte) (consumed int, err error) {\n\top := Operation{}\n\tfor len(data) > 0 {\n\t\tn, uerr := op.Unserialize(data)\n\t\tif uerr == nil {\n\t\t\tconsumed += n\n\t\t\tdata = data[n:]\n\t\t\tif err = self.rsync.ApplyDelta(self.delta_output, self.delta_input, op); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif op.Type == OpData {\n\t\t\t\tself.total_data_in_delta += len(op.Data)\n\t\t\t}\n\t\t} else {","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/kovidgoyal/kitty/blob/6d5d0c440603ad9bdf6dcd599f73f6dde21acb44/tools/rsync/api.go#L110-L146","documentation":"FinishSignatureData succeeded in consuming all bytes but no valid header was ever parsed, so the rsync hasher (block size + hash config) was never initialized.","triggerScenarios":"Calling FinishSignatureData before AddSignatureData has processed the 12-byte signature header — including calling it immediately with no data at all.","commonSituations":"Empty signature stream (peer sent nothing), or calling the API out of order (finish before add), or the header arriving only after finish was already invoked.","solutions":["Ensure AddSignatureData receives the header bytes first, then block data, then call FinishSignatureData","Check the sender actually transmitted a signature","If the stream is legitimately empty, skip delta creation rather than finishing an empty differ"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if totalSignatureBytesReceived == 0 { skip delta creation; empty signature }","typeGuard":"func hasSignatureHeader(received int) bool { return received >= 12 }","tryCatchPattern":"if err := differ.FinishSignatureData(); err != nil { if strings.Contains(err.Error(), \"No header\") { handle empty source } else { return err } }","preventionTips":["Call AddSignatureData with header bytes before FinishSignatureData","Short-circuit the empty-source case before diffing"],"tags":["rsync","api-misuse","ordering"],"backgroundTag":"api-call-order","analyzedSha":"6d5d0c440603ad9bdf6dcd599f73f6dde21acb44","analyzedAt":"2026-08-27T14:20:20.142Z","schemaVersion":2},"datasetVersion":"2026-08-27T19:17:21.184Z"}