{"record":{"id":"f01addbf2f18d3f6","repo":"siyuan-note/siyuan","slug":"invalid-trailing-encrypted-asset-data","errorCode":null,"errorMessage":"invalid trailing encrypted asset data","messagePattern":"invalid trailing encrypted asset data","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/crypto.go","lineNumber":2497,"sourceCode":"\t\twritten += int64(n)\n\t\tzeroAndClear(plainChunk)\n\t\tif writeErr != nil {\n\t\t\treturn \"\", writeErr\n\t\t}\n\t\tif n != len(plainChunk) {\n\t\t\treturn \"\", io.ErrShortWrite\n\t\t}\n\t}\n\tvar terminator uint32\n\tif err = binary.Read(reader, binary.BigEndian, &terminator); err != nil {\n\t\treturn \"\", err\n\t}\n\tif terminator != 0 || written != metadata.Size {\n\t\treturn \"\", errors.New(\"invalid encrypted asset content length\")\n\t}\n\tvar trailing [1]byte\n\tif _, trailingErr := io.ReadFull(reader, trailing[:]); trailingErr != io.EOF {\n\t\treturn \"\", errors.New(\"invalid trailing encrypted asset data\")\n\t}\n\treturn metadata.OriginalName, nil\n}\n\n// DecryptAsset 对应解密。\nfunc DecryptAsset(boxID, diskName string, dek, ciphertext []byte) ([]byte, error) {\n\tplaintext, _, err := DecryptAssetWithName(boxID, diskName, dek, ciphertext)\n\treturn plaintext, err\n}\n\n// notebookCryptoBackupPath 返回加密笔记本的独立 BoxCrypt 备份路径。\n// 该文件在主 conf.json 丢失时用作\"此笔记本是加密笔记本\"的标识和降级恢复源。\n// 与全局 NotebookCrypto 备份（<DataDir>/.siyuan/data-crypto-backup.json）配合使用，\n// 全局备份存 MasterSalt/KEKVerifier，per-notebook 备份存 WrappedDEK/WrapNonce。\nconst notebookCryptoBackupFilename = \"notebook-crypto-backup.json\"\n\nfunc notebookCryptoBackupPath(boxID string) string {\n\treturn filepath.Join(util.DataDir, boxID, \".siyuan\", notebookCryptoBackupFilename)","sourceCodeStart":2479,"sourceCodeEnd":2515,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/crypto.go#L2479-L2515","documentation":"Even after content length checks pass, the function requires the reader to be exactly at EOF (one trailing read must return io.EOF). Any extra byte after the trailer yields this error, guaranteeing the decrypted file consumed the entire encrypted stream with no appended garbage.","triggerScenarios":"DecryptAssetToWriter reads one byte past the trailer; if io.ReadFull returns anything other than io.EOF (a byte, or an I/O error other than EOF), the error is returned. Caused by data concatenated after the encrypted asset (e.g. two files concatenated, or an appended signature/nonce).","commonSituations":"Backup scripts concatenating files; a writer version appending extra fields the reader doesn't know; copy/paste of files including extra bytes.","solutions":["Inspect the bytes after the trailer and remove any appended data, then retry","Restore a clean copy of the asset from backup or sync","Ensure the encrypting tool and reading tool use the same format version (no extra trailer fields)","Re-encrypt the original plaintext if the appended bytes cannot be identified"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if _, err := model.DecryptAsset(box, disk, dek, ct); err != nil {\n    if strings.Contains(err.Error(), \"trailing\") {\n        // inspect and strip appended bytes, or restore a clean copy\n        return restoreCleanCopy(disk)\n    }\n    return err\n}","preventionTips":["Never concatenate or append data to encrypted asset files","Copy files byte-for-byte (no text-mode or encoding-converting transfers)"],"tags":["encryption","format-validation","trailing-data"],"backgroundTag":"invalid-argument-format","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}