{"record":{"id":"ddb61d735ffe425b","repo":"siyuan-note/siyuan","slug":"w-path-is-empty","errorCode":null,"errorMessage":"%w: path is empty","messagePattern":"%w: path is empty","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/import_obsidian.go","lineNumber":560,"sourceCode":"\t\t\t}\n\t\t\tret.Analysis.Warnings = append(ret.Analysis.Warnings, asset.Source.RelPath)\n\t\t\tcontinue\n\t\t}\n\t\tret.ImportAssets[key] = asset\n\t\tret.Analysis.ImportableAssetCount++\n\t\tret.Analysis.ImportableAssetSize += asset.Source.Size\n\t}\n\tret.Analysis.UnreferencedFileCount = len(ret.ImportAssets) - len(ret.ReferencedAssets)\n\tif ret.Analysis.UnreferencedFileCount < 0 {\n\t\tret.Analysis.UnreferencedFileCount = 0\n\t}\n\tprogress(100, \"Analysis completed\")\n\treturn ret, nil\n}\n\nfunc validateObsidianVaultRoot(localPath string) (string, error) {\n\tif strings.TrimSpace(localPath) == \"\" {\n\t\treturn \"\", fmt.Errorf(\"%w: path is empty\", errObsidianVaultUnreadable)\n\t}\n\tabs, err := filepath.Abs(filepath.Clean(localPath))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"%w: normalize Vault path: %v\", errObsidianVaultUnreadable, err)\n\t}\n\tinfo, err := os.Lstat(abs)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"%w: read Vault root: %v\", errObsidianVaultUnreadable, err)\n\t}\n\tif !info.IsDir() {\n\t\treturn \"\", errObsidianVaultNotDirectory\n\t}\n\tif info.Mode()&os.ModeSymlink != 0 || isObsidianResolvedLink(abs) {\n\t\treturn \"\", fmt.Errorf(\"%w: Vault root is a symbolic link or reparse point\", errObsidianVaultUnsafePath)\n\t}\n\tif util.IsSensitivePath(abs) {\n\t\treturn \"\", fmt.Errorf(\"%w: selected Vault path is sensitive\", errObsidianVaultUnsafePath)\n\t}","sourceCodeStart":542,"sourceCodeEnd":578,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/import_obsidian.go#L542-L578","documentation":"validateObsidianVaultRoot rejects an empty/whitespace-only Vault local path by wrapping errObsidianVaultUnreadable with 'path is empty'. The caller must supply a concrete filesystem path to the Obsidian vault directory before analysis can begin.","triggerScenarios":"Calling the Obsidian analyze/import API with localPath == \"\" or only whitespace at kernel/model/import_obsidian.go:560 — e.g. the folder-picker returned nothing or a config field was never filled in.","commonSituations":"Frontend sends an unset path because the user skipped choosing a vault; a saved settings file lost the vaultPath key; automation passed an env variable that is empty.","solutions":["Pass the absolute path of the Obsidian vault directory (the folder containing .obsidian) in the request","Validate the path is non-empty on the client before invoking the API","If path comes from settings/env, check the value exists before starting the import","Re-open the vault picker in the UI and confirm the selection"],"exampleFix":"// before\nanalyzeObsidianVault(\"\") // throws: path is empty\n// after\nif vaultPath == \"\" {\n    return errors.New(\"vault path is required\")\n}\nanalyzeObsidianVault(vaultPath)","handlingStrategy":"validation","validationCode":"if strings.TrimSpace(vaultPath) == \"\" {\n    return errors.New(\"vault path is required\")\n}","typeGuard":"null","tryCatchPattern":"_, err := AnalyzeObsidianVault(vaultPath)\nif err != nil && strings.Contains(err.Error(), \"path is empty\") {\n    promptUserToPickVaultFolder()\n}","preventionTips":["Validate the path input client-side before calling the API","Persist vaultPath settings and check them on startup","Never pass empty strings from automation; fail fast with a clear message"],"tags":["obsidian-import","empty-path","validation","input-validation"],"backgroundTag":"empty-required-field","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"}