{"record":{"id":"c9b76bb68a2dcfe2","repo":"siyuan-note/siyuan","slug":"invalid-encrypted-asset-chunk-count","errorCode":null,"errorMessage":"invalid encrypted asset chunk count","messagePattern":"invalid encrypted asset chunk count","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/crypto.go","lineNumber":2358,"sourceCode":"\t// 仅认证元数据同时缺少两个版本字段时按旧容器读取，显式空值或不完整的新格式不能降级。\n\tif len(version.Spec) == 0 && len(version.ContainerID) == 0 {\n\t\tmetadata.Spec = encryptedAssetLegacySpec\n\t} else if metadata.Spec != encryptedAssetSpec || len(metadata.ContainerID) != encryptedAssetContainerIDSize {\n\t\treturn nil, errors.New(\"unsupported encrypted asset container version\")\n\t}\n\tif metadata.OriginalName == \"\" || metadata.OriginalName == \".\" ||\n\t\tfilepath.Base(metadata.OriginalName) != metadata.OriginalName || strings.ContainsAny(metadata.OriginalName, `/\\`) {\n\t\treturn nil, errors.New(\"invalid encrypted asset original name\")\n\t}\n\tif metadata.Size < 0 {\n\t\treturn nil, errors.New(\"invalid encrypted asset content metadata\")\n\t}\n\tchunks := uint64(metadata.Size) / encryptedAssetChunkSize\n\tif metadata.Size%encryptedAssetChunkSize != 0 || metadata.Size == 0 {\n\t\tchunks++\n\t}\n\tif metadata.Chunks != chunks {\n\t\treturn nil, errors.New(\"invalid encrypted asset chunk count\")\n\t}\n\treturn metadata, nil\n}\n\n// DecryptAssetWithName 解密资源内容并返回原始名称。\nfunc DecryptAssetWithName(boxID, diskName string, dek, ciphertext []byte) (plaintext []byte, originalName string, err error) {\n\tvar output bytes.Buffer\n\toriginalName, err = DecryptAssetToWriter(boxID, diskName, dek, bytes.NewReader(ciphertext), &output)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\treturn output.Bytes(), originalName, nil\n}\n\n// DecryptAssetName 只解密资源的名称元数据，不处理资源内容。\nfunc DecryptAssetName(boxID, diskName string, dek, ciphertext []byte) (originalName string, err error) {\n\tmetadata, _, err := decryptAssetMetadata(boxID, diskName, dek, ciphertext)\n\tif err != nil {","sourceCodeStart":2340,"sourceCodeEnd":2376,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/crypto.go#L2340-L2376","documentation":"decryptAssetMetadata recomputes the expected chunk count from metadata.Size (ceil division by encryptedAssetChunkSize) and requires it to equal metadata.Chunks. A mismatch means the chunk bookkeeping disagrees with the declared content size, so the container layout cannot be trusted.","triggerScenarios":"Decrypting a container where the Chunks field was computed with different chunking rules (e.g. different encryptedAssetChunkSize) than the reader, or where Size/Chunks disagree due to corruption or a buggy writer.","commonSituations":"Containers produced by a different version where chunk size changed, third-party generators miscounting chunks, corrupted metadata fields after storage damage.","solutions":["Re-encrypt the asset with the current kernel so Size and Chunks are recomputed consistently","Verify the writer and reader use the same encryptedAssetChunkSize","Restore the asset from a backup taken with the matching format version","If a version change caused this, migrate assets by decrypt+re-encrypt before switching chunk size"],"exampleFix":"// before: chunks counted with a hardcoded size\nmeta.Chunks = uint64(math.Ceil(float64(size) / (1 << 20)))\n// after: use the shared constant\nmeta.Chunks = uint64(size) / encryptedAssetChunkSize\nif size%encryptedAssetChunkSize != 0 { meta.Chunks++ }","handlingStrategy":"validation","validationCode":"expectedChunks := uint64(size) / encryptedAssetChunkSize\nif size%encryptedAssetChunkSize != 0 || size == 0 { expectedChunks++ }\nif meta.Chunks != expectedChunks {\n    return errors.New(\"chunk count mismatch; re-encrypt the asset\")\n}","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"chunk count\") {\n    // re-encrypt from source or restore from a matching-version backup\n}","preventionTips":["Derive chunk counts from the shared encryptedAssetChunkSize constant, never hardcoded values","When changing chunk size, migrate assets with decrypt+re-encrypt","Write and read with the same kernel version when possible"],"tags":["encryption","assets","validation","chunking","go-kernel"],"backgroundTag":"schema-validation-failed","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}