{"record":{"id":"88328ec3a30e169e","repo":"OpenNHP/opennhp","slug":"invalid-ztdo-file","errorCode":null,"errorMessage":"invalid ztdo file","messagePattern":"invalid ztdo file","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nhp/core/ztdo/ztdo.go","lineNumber":458,"sourceCode":"\n\t\tpayloadBuf := toBuffer(payload)\n\t\trecalcSig.mixHash(payloadBuf)\n\t\tremainingCiphertextFileSize -= int64(payloadBuf.Len())\n\n\t\tplaintext, err := payload.GetPlainText(ztdo.header.GetCipherMode(), gcmKey, ad)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif _, err := plaintextFile.Write(plaintext); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif ztdo.header.HasSignature() {\n\t\t\tif remainingCiphertextFileSize == SIGNATURELenSize {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif remainingCiphertextFileSize == 0 {\n\t\t\t\treturn fmt.Errorf(\"invalid ztdo file\")\n\t\t\t}\n\t\t} else {\n\t\t\tif remainingCiphertextFileSize == 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\t// update recalculated signature\n\trecalcSig.sign(gcmKey)\n\n\tif ztdo.header.HasSignature() {\n\t\tif err := toStructure(ciphertextFile, &ztdo.signature); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif !ztdo.signature.verify(recalcSig) {\n\t\t\treturn fmt.Errorf(\"signature verification failed\")","sourceCodeStart":440,"sourceCodeEnd":476,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/nhp/core/ztdo/ztdo.go#L440-L476","documentation":"DecryptZtdoFile walks a ztdo file chunk by chunk after decrypting. When the header declares a signature, the file must contain exactly SIGNATURELenSize bytes remaining (the signature) once the ciphertext is consumed. If the header says a signature exists but the file ends with 0 bytes remaining, there is no signature block at all, so the file is structurally invalid and this error is thrown.","triggerScenarios":"Calling DecryptZtdoFile on a ztdo file whose header flag says HasSignature()==true but whose ciphertext section ends exactly at EOF with no signature bytes following — i.e. a truncated file where the signature block was cut off.","commonSituations":"Incomplete download or interrupted copy of a .ztdo file; a writer that set the signature flag but crashed/was killed before appending the signature; manual truncation or corruption during transfer; mixing a signed header with an unsigned file body from different versions.","solutions":["Re-acquire the ztdo file from its source — the file is truncated and unrecoverable in place.","Verify the file size against the expected size (header + ciphertext chunks + SIGNATURELenSize) before decrypting.","If you produce these files, ensure the signature block is written after encryption and before closing the file.","If the file was intentionally unsigned, regenerate it with the signature flag cleared so the header matches the actual content."],"exampleFix":"// before: blind decrypt\nerr := ztdo.DecryptZtdoFile(file, passphrase)\n// after: sanity-check size first\nfi, _ := file.Stat()\nif fi.Size() < headerSize+SIGNATURELenSize {\n    return fmt.Errorf(\"ztdo file truncated: %d bytes\", fi.Size())\n}\nerr := ztdo.DecryptZtdoFile(file, passphrase)","handlingStrategy":"validation","validationCode":"fi, err := f.Stat()\nif err != nil { return err }\nif fi.Size() < int64(headerSize+SIGNATURELenSize) {\n    return fmt.Errorf(\"ztdo file too small (%d bytes): signature block missing\", fi.Size())\n}","typeGuard":null,"tryCatchPattern":"if err := ztdo.DecryptZtdoFile(f, pass); err != nil {\n    if strings.Contains(err.Error(), \"invalid ztdo file\") {\n        // recover: re-fetch or regenerate the file\n        return errReacquireFile\n    }\n    return err\n}","preventionTips":["Check file size against header+signature expectations before decrypting","Verify integrity (checksum) of downloads before processing","Ensure writers append the signature block before closing the file"],"tags":["ztdo","file-corruption","truncated-file","signature"],"backgroundTag":"checksum-mismatch","analyzedSha":"6e04ca5ff03222a699c24205cd4bf8fee9af7ffe","analyzedAt":"2026-09-07T15:44:59.941Z","contentChangedAt":"2026-09-07T15:44:59.941Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}