{"record":{"id":"df0f46ab19b97568","repo":"denoland/deno","slug":"invalidinput","errorCode":"InvalidInput","errorMessage":"unable to open database file: \"{}\" is a symlink","messagePattern":"unable to open database file: \"(.+?)\" is a symlink","errorType":"error_code","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"ext/kv/sqlite.rs","lineNumber":92,"sourceCode":"  }\n  path.to_path_buf()\n}\n\n/// SQLite does not enforce `SQLITE_OPEN_NOFOLLOW` on Windows (its\n/// `winFullPathname` never resolves reparse points), so reject symlinks and\n/// junctions in every path component manually before opening.\n#[cfg(windows)]\nfn refuse_reparse_point_components(path: &Path) -> std::io::Result<()> {\n  let mut current = PathBuf::new();\n  for component in path.components() {\n    current.push(component);\n    #[allow(\n      clippy::disallowed_methods,\n      reason = \"the database path is always on the real fs\"\n    )]\n    match std::fs::symlink_metadata(&current) {\n      Ok(metadata) if metadata.file_type().is_symlink() => {\n        return Err(std::io::Error::new(\n          std::io::ErrorKind::InvalidInput,\n          format!(\n            \"unable to open database file: \\\"{}\\\" is a symlink\",\n            current.display()\n          ),\n        ));\n      }\n      Ok(_) => {}\n      // Missing components are created (or rejected) by SQLite itself.\n      Err(_) => break,\n    }\n  }\n  Ok(())\n}\n\n#[async_trait(?Send)]\nimpl DatabaseHandler for SqliteDbHandler {\n  type DB = denokv_sqlite::Sqlite;","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/denoland/deno/blob/336da420f4343cbb1dcbd5eed9d075ff555ed6ee/ext/kv/sqlite.rs#L74-L110","documentation":"ext/kv's sqlite backend refuses to open a database file if any component of its path is a symbolic link. refuse_reparse_point_components walks each path component with symlink_metadata; on encountering a symlink it returns this InvalidInput io::Error naming the offending component, as a security hardening measure since the KV database path is expected to be on the real filesystem.","triggerScenarios":"Opening a KV sqlite database (--unstable-kv / Deno.openKv) whose path, or any ancestor directory, is a symlink — e.g. dbPath \"/data/link/db.sqlite\" where \"link\" or \"db.sqlite\" is a symlink.","commonSituations":"macOS /tmp being a symlink to /private/tmp; symlinked dotfile-managed config directories (e.g. Dropbox/chezmoi managed DENO_DIR); container setups where the data dir is a symlink into a volume.","solutions":["Use the real, resolved path (macOS: replace /tmp with /private/tmp; generally resolve with realpath) when opening the KV database.","Remove the symlink and replace it with a real directory or bind mount.","Point DENO_DIR or the openKv path at a non-symlinked location.","If symlinks are intentional, avoid the sqlite KV backend for that path (use a different storage location)."],"exampleFix":"// before\nconst db = await Deno.openKv(\"/tmp/kv.sqlite\"); // /tmp is a symlink on macOS\n// after\nconst real = await Deno.realPath(\"/tmp\");\nconst db = await Deno.openKv(`${real}/kv.sqlite`);","handlingStrategy":"validation","validationCode":"const st = await Deno.lstat(p);\nif (st.isSymlink) throw new Error(`${p} is a symlink; resolve it first`);\nconst real = await Deno.realPath(p);\n// pass `real` to Deno.openKv","typeGuard":"function isNotSymlink(st: Deno.FileInfo): boolean { return !st.isSymlink; }","tryCatchPattern":"try {\n  kv = await Deno.openKv(path);\n} catch (e) {\n  if (String(e).includes(\"is a symlink\")) {\n    kv = await Deno.openKv(await Deno.realPath(path));\n  } else throw e;\n}","preventionTips":["Resolve symlinks (realPath) before opening KV databases.","Remember macOS /tmp is a symlink to /private/tmp.","Avoid symlinked dotfile-managed directories for DENO_DIR.","Use bind mounts instead of symlinks for container data volumes."],"tags":["kv","sqlite","symlink","security","filesystem"],"backgroundTag":"path-traversal-blocked","analyzedSha":"336da420f4343cbb1dcbd5eed9d075ff555ed6ee","analyzedAt":"2026-09-11T17:12:50.272Z","contentChangedAt":"2026-09-11T17:12:50.272Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}