{"record":{"id":"75fc51920c3262d2","repo":"siyuan-note/siyuan","slug":"parse-file-annotation-s-w","errorCode":null,"errorMessage":"parse file annotation [%s]: %w","messagePattern":"parse file annotation \\[(.+?)\\]: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/export.go","lineNumber":4390,"sourceCode":"\tsyaData, readErr := os.ReadFile(sya)\n\tif readErr != nil {\n\t\treturn fmt.Errorf(\"read file annotation [%s]: %w\", sya, readErr)\n\t}\n\tif nil != dek {\n\t\tplain, decErr := DecryptAsset(assetBoxID, filepath.Base(sya), dek, syaData)\n\t\tif decErr != nil {\n\t\t\treturn fmt.Errorf(\"decrypt file annotation [%s]: %w\", sya, decErr)\n\t\t}\n\t\tsyaData = plain\n\t}\n\tsyaJSON := map[string]struct {\n\t\tPages []struct {\n\t\t\tIndex *int `json:\"index\"`\n\t\t} `json:\"pages\"`\n\t\tPage *int `json:\"page\"`\n\t}{}\n\tif err = gulu.JSON.UnmarshalJSON(syaData, &syaJSON); err != nil {\n\t\treturn fmt.Errorf(\"parse file annotation [%s]: %w\", sya, err)\n\t}\n\tannotationData, found := syaJSON[annotationID]\n\tpageIndex := annotationData.Page\n\tif 0 < len(annotationData.Pages) {\n\t\tpageIndex = annotationData.Pages[0].Index\n\t}\n\tif !found || nil == pageIndex || *pageIndex < 0 {\n\t\treturn fmt.Errorf(\"missing or invalid annotation [%s] in [%s]\", annotationID, sya)\n\t}\n\tpageStr := strconv.Itoa(*pageIndex + 1)\n\n\trefText := n.TextMarkTextContent\n\text := filepath.Ext(p)\n\tfile := strings.TrimPrefix(strings.TrimSuffix(p, ext), \"assets/\")\n\t// 仅剥离完整的节点 ID 后缀，无后缀文件及短文件名保持原样。\n\tif len(file) > 23 && file[len(file)-23] == '-' && ast.IsNodeIDPattern(file[len(file)-22:]) {\n\t\tfile = file[:len(file)-23]\n\t}","sourceCodeStart":4372,"sourceCodeEnd":4408,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/export.go#L4372-L4408","documentation":"The .sya file content must be a JSON object mapping annotation IDs to page descriptors. gulu.JSON.UnmarshalJSON failure is wrapped as this parse error and aborts the export. This means the sidecar exists but its content is not valid JSON of the expected shape.","triggerScenarios":"Exporting an annotation where the .sya file was hand-edited and broke JSON syntax, truncated during transfer, saved with wrong encoding (BOM/UTF-16), or written by an incompatible tool version with a different schema.","commonSituations":"Manual editing of sidecar files; sync tools that merge .sya files textually; disk corruption; third-party annotation writers producing divergent JSON.","solutions":["Open the .sya file and fix or restore valid JSON of the expected {annotationID: {pages:[{index}], page}} shape","Re-sync or restore the .sya from a version history snapshot","Delete the corrupt sidecar and recreate the annotation in the PDF so a fresh .sya is written","Check the file encoding is plain UTF-8 without BOM"],"exampleFix":"// before: broken sidecar\n{\"ann-1\": {\"page\": oops\n// after: valid sidecar\n{\"ann-1\": {\"page\": 1}}","handlingStrategy":"validation","validationCode":"// validate sidecar JSON shape before export\nfunction isValidSya(text) {\n  try {\n    const obj = JSON.parse(text);\n    return typeof obj === \"object\" && obj !== null &&\n      Object.values(obj).every(v => v && (typeof v.page === \"number\" || Array.isArray(v.pages)));\n  } catch { return false; }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await exportAnnotation(docID);\n} catch (e) {\n  if (/parse file annotation/.test(e.message)) {\n    // fall back to a version-history snapshot of the .sya\n    await restoreFileVersion(assetPath + \".sya\");\n  }\n}","preventionTips":["Do not hand-edit .sya files; use the app's annotation UI","Exclude .sya files from textual merge tools / sync conflict resolution","Keep sidecar files as UTF-8 without BOM"],"tags":["export","json","file-annotation","parse"],"backgroundTag":"json-parse-error","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}