Error reference
errors dvs can raise, shown side by side for R and the CLI
This page is implementation detail, beyond what normal use requires.
🔗Setup
library(dvs)
library(here)
# Print tibbles wide so all columns appear on one row.
# 10000 is the ceiling base R allows.
options(width = 10000)
Create a temporary storage directory and project root, then expose them to the
shell so the bash chunks below can use $DVS_PROJECT and $DVS_STORAGE.
storage <- basename(tempfile(fileext = "_storage"))
new_project <- basename(tempfile(fileext = "_project"))
dir.create(here::here(storage))
dir.create(here::here(new_project))
# Make it a git repo so dvs can find the project root
system2("git", c("init", here::here(new_project)), stdout = FALSE, stderr = FALSE)
Sys.setenv(DVS_PROJECT = here::here(new_project), DVS_STORAGE = here::here(storage))🔗init errors
🔗Storage path is inside the repository
Passing a storage path that resolves to a subdirectory of the project root is rejected. dvs refuses to version-control its own blob store.
setwd(here::here(new_project))
dvs_init(storage_path = here::here(new_project, "inside_storage"),
root_dir = here::here(new_project))Error in `dvs_init_impl()`:
! The given storage path is within the repository.cd "$DVS_PROJECT"
dvs init "$DVS_PROJECT/inside_storage"Error: The given storage path is within the repository.🔗Project already initialised: dvs.toml exists
Once dvs.toml is present a second init call fails immediately.
# First init succeeds
setwd(here::here(new_project))
dvs_init(storage_path = here::here(storage), root_dir = here::here(new_project))# A tibble: 1 × 4
compression metadata_folder_name backend_path backend_group
<chr> <lgl> <chr> <chr>
1 zstd NA /Users/elea/Documents/a2ai_github/dvs2-demo-repo/file774f3a8a5fc6_storage staff # Second init fails
dvs_init(storage_path = here::here(storage), root_dir = here::here(new_project))Error in `dvs_init_impl()`:
! dvs is already initialized (dvs.toml exists)# dvs.toml already created by the R chunk above
cd "$DVS_PROJECT"
dvs init "$DVS_STORAGE"Error: dvs is already initialized (dvs.toml exists)🔗Backend storage already exists
If dvs.toml is deleted but the storage directory still contains a previous
backend layout, re-running init refuses rather than silently reusing the
backend.
# Use fresh temp dirs so we don't disrupt the main demo project
bse_storage <- basename(tempfile(fileext = "_storage"))
bse_project <- basename(tempfile(fileext = "_project"))
dir.create(here::here(bse_storage)); dir.create(here::here(bse_project))
system2("git", c("init", here::here(bse_project)), stdout = FALSE, stderr = FALSE)
setwd(here::here(bse_project))
dvs_init(storage_path = here::here(bse_storage), root_dir = here::here(bse_project))# A tibble: 1 × 4
compression metadata_folder_name backend_path backend_group
<chr> <lgl> <chr> <chr>
1 zstd NA /Users/elea/Documents/a2ai_github/dvs2-demo-repo/file774f4a84b4ee_storage staff # Remove dvs.toml but leave the storage intact
file.remove(here::here(bse_project, "dvs.toml"))[1] TRUE# Second init detects the existing backend
dvs_init(storage_path = here::here(bse_storage), root_dir = here::here(bse_project))Error in `dvs_init_impl()`:
! dvs is already initialized (backend storage exists)BSE_STORAGE=$(mktemp -d)
BSE_PROJECT=$(mktemp -d)
git -C "$BSE_PROJECT" init -q
cd "$BSE_PROJECT"
dvs init "$BSE_STORAGE" > /dev/null
rm dvs.toml
dvs init "$BSE_STORAGE"
rm -rf "$BSE_STORAGE" "$BSE_PROJECT"Error: dvs is already initialized (backend storage exists)🔗add errors
🔗Not inside a DVS project
Calling dvs_add / dvs add from a directory that has no dvs.toml anywhere
in its ancestor chain fails before touching the filesystem.
outside <- basename(tempfile())
dir.create(here::here(outside))
setwd(here::here(outside))
dvs_add(paths = "anything.csv")Error in `dvs_add_impl()`:
! Not in a DVS repositoryOUTSIDE=$(mktemp -d)
cd "$OUTSIDE"
dvs add anything.csv
rm -rf "$OUTSIDE"Error: Not in a DVS repository🔗No files to add: empty glob
A glob pattern that matches no files raises in the R package; the CLI prints
Error: No files to add and exits 1, so pipelines see the failure too.
setwd(here::here(new_project))
dvs_add(glob = "data/*.csv")Error in `dvs_add_impl()`:
! No files to addcd "$DVS_PROJECT"
dvs add --glob "data/*.csv"Error: No files to add🔗Path not found
Passing an explicit path that doesn't exist on disk aborts before any files are processed. Neither surface does partial work.
setwd(here::here(new_project))
dvs_add(paths = "nonexistent_file.csv")Error in `dvs_add_impl()`:
! Path not found: nonexistent_file.csvcd "$DVS_PROJECT"
dvs add nonexistent_file.csvError: Path not found: nonexistent_file.csv🔗Path resolves to a directory (no glob)
Passing a bare directory name with no glob is an invalid input, not an empty
match. Both surfaces refuse the batch with the same message (path is a directory); the CLI exits 1. Directories only make sense combined with a
glob, or via --recursive on status and get.
setwd(here::here(new_project))
dir.create(here::here(new_project, "mydir"), showWarnings = FALSE)
dvs_add(paths = "mydir")Error in `dvs_add_impl()`:
! Refusing to add, the following paths are invalid:
mydir: path is a directorycd "$DVS_PROJECT"
mkdir -p mydir
dvs add mydirError: Refusing to add, the following paths are invalid:
mydir: path is a directory🔗Path is outside the project root
You're inside the project but ask dvs to track a file that lives elsewhere on
disk, by absolute path. This is an invalid input: the whole batch is refused
before anything is written, and the R package raises the same way it does for
a missing path. (Earlier releases recorded this as an error row in the
result instead; 0.4.0 moved it to batch validation.)
setwd(here::here(new_project))
outside_file <- tempfile(fileext = ".csv")
writeLines("a,b,c", outside_file)
dvs_add(paths = outside_file)Error in `dvs_add_impl()`:
! Refusing to add, the following paths are invalid:
/var/folders/_x/bq8vb1b156sgl363l71by61h0000gn/T//RtmphOMKU8/file774f19adc335.csv: path is outside projectcd "$DVS_PROJECT"
OUTSIDE_FILE=$(mktemp -t dvs_outside).csv
echo "a,b,c" > "$OUTSIDE_FILE"
dvs add "$OUTSIDE_FILE"
rm -f "$OUTSIDE_FILE"Error: Refusing to add, the following paths are invalid:
/var/folders/_x/bq8vb1b156sgl363l71by61h0000gn/T/dvs_outside.eMDZyPnyvz.csv: path is outside project🔗Mixed paths: one exists, one does not
When only some paths exist, both surfaces abort at the first missing path. No partial add.
setwd(here::here(new_project))
writeLines("x", here::here(new_project, "present.csv"))
dvs_add(paths = c("present.csv", "missing.csv"))Error in `dvs_add_impl()`:
! Path not found: missing.csvcd "$DVS_PROJECT"
echo "x" > present.csv
dvs add present.csv missing.csvError: Path not found: missing.csv🔗status errors
🔗Not inside a DVS project
dvs_status / dvs status require a dvs.toml in the ancestor tree.
outside2 <- basename(tempfile())
dir.create(here::here(outside2))
setwd(here::here(outside2))
dvs_status()Error in `dvs_status_impl()`:
! Not in a DVS repositoryOUTSIDE2=$(mktemp -d)
cd "$OUTSIDE2"
dvs status
rm -rf "$OUTSIDE2"Error: Not in a DVS repository🔗Path outside the project root
Unlike add, an absolute path outside the project does not produce an
error for status. The path just doesn't match anything tracked and you
get the empty-set response.
setwd(here::here(new_project))
outside_status <- tempfile(fileext = ".csv")
writeLines("a,b,c", outside_status)
dvs_status(paths = outside_status) |> print(width = Inf, n = Inf)# A tibble: 0 × 0cd "$DVS_PROJECT"
OUTSIDE_S=$(mktemp -t dvs_outside_s).csv
echo "a,b,c" > "$OUTSIDE_S"
dvs status "$OUTSIDE_S"
rm -f "$OUTSIDE_S"No tracked files🔗Mixed paths: one tracked, one does not exist
Unlike add, status quietly drops paths it can't match. You get a tibble
with only the tracked path and no error or warning about the missing one.
setwd(here::here(new_project))
# Track one real file so we have something to match against
writeLines("x", here::here(new_project, "present.csv"))
dvs_add(paths = "present.csv")# A tibble: 1 × 5
path outcome hash size stored_size
<chr> <chr> <chr> <bytes> <bytes>
1 present.csv copied 44c77418e27569db9213c6b43d9049ecffb5496f7d0e3d4254bb68410adecc3e 2 B 11 Bdvs_status(paths = c("present.csv", "missing.csv")) |>
print(width = Inf, n = Inf)# A tibble: 1 × 7
path status hash size created_by compression add_time
<chr> <chr> <chr> <bytes> <chr> <chr> <dttm>
1 present.csv current 44c77418e27569db9213c6b43d9049ecffb5496f7d0e3d4254bb68410adecc3e 2 B elea zstd 2026-08-10 19:44:33cd "$DVS_PROJECT"
dvs status present.csv missing.csv+-------------+---------+------+
| path | status | size |
+-------------+---------+------+
| present.csv | current | 2 B |
+-------------+---------+------+🔗Unparseable meta file
If a .dvs meta file is corrupted (non-JSON content), the parse error
surfaces as a per-file row in the result rather than a top-level abort. The
rest of the tree still resolves.
# Fresh project so we can corrupt one meta file without affecting the rest
ump_storage <- basename(tempfile(fileext = "_storage"))
ump_project <- basename(tempfile(fileext = "_project"))
dir.create(here::here(ump_storage)); dir.create(here::here(ump_project))
system2("git", c("init", here::here(ump_project)), stdout = FALSE, stderr = FALSE)
setwd(here::here(ump_project))
dvs_init(storage_path = here::here(ump_storage), root_dir = here::here(ump_project))# A tibble: 1 × 4
compression metadata_folder_name backend_path backend_group
<chr> <lgl> <chr> <chr>
1 zstd NA /Users/elea/Documents/a2ai_github/dvs2-demo-repo/file774f34580c9c_storage staff writeLines("x", here::here(ump_project, "corrupt.csv"))
dvs_add(paths = "corrupt.csv")# A tibble: 1 × 5
path outcome hash size stored_size
<chr> <chr> <chr> <bytes> <bytes>
1 corrupt.csv copied 44c77418e27569db9213c6b43d9049ecffb5496f7d0e3d4254bb68410adecc3e 2 B 11 B# Corrupt the meta file
writeLines("garbage", here::here(ump_project, ".dvs", "corrupt.csv.dvs"))
dvs_status() |> print(width = Inf, n = Inf)# A tibble: 1 × 3
path error add_time
<chr> <chr> <dttm>
1 corrupt.csv expected value at line 1 column 1 NA Sys.setenv(UMP_PROJECT = here::here(ump_project))cd "$UMP_PROJECT"
dvs statusError getting status for corrupt.csv: expected value at line 1 column 1
+------+--------+------+
| path | status | size |
+------+--------+------+
Error: Some files failed to get status🔗get errors
🔗Not inside a DVS project
outside3 <- basename(tempfile())
dir.create(here::here(outside3))
setwd(here::here(outside3))
dvs_get(paths = "something.csv")Error in `dvs_get_impl()`:
! Not in a DVS repositoryOUTSIDE3=$(mktemp -d)
cd "$OUTSIDE3"
dvs get something.csv
rm -rf "$OUTSIDE3"Error: Not in a DVS repository🔗No files to get: empty glob
When the glob resolves to zero tracked files, dvs get aborts.
setwd(here::here(new_project))
dvs_get(glob = "data/nope_*.csv")Error in `dvs_get_impl()`:
! No files to getcd "$DVS_PROJECT"
dvs get --glob "data/nope_*.csv"Error: No files to get🔗File is not tracked by DVS
Requesting a file that exists on disk but has never been dvs add-ed.
setwd(here::here(new_project))
writeLines("col1,col2", here::here(new_project, "untracked.csv"))
dvs_get(paths = "untracked.csv")Error in `dvs_get_impl()`:
! The following paths are not tracked by DVS:
untracked.csvcd "$DVS_PROJECT"
echo "col1,col2" > untracked.csv
dvs get untracked.csvError: The following paths are not tracked by DVS:
untracked.csv🔗File is not found at all
Requesting a path that neither exists on disk nor has metadata.
setwd(here::here(new_project))
dvs_get(paths = "ghost.csv")Error in `dvs_get_impl()`:
! The following paths are not tracked by DVS:
ghost.csvcd "$DVS_PROJECT"
dvs get ghost.csvError: The following paths are not tracked by DVS:
ghost.csv🔗Storage blob missing for tracked file
The metadata .dvs file exists but the corresponding blob has been deleted
from storage, simulating a corrupted or partially-synced store.
# Use a fresh project; the main demo project still has live state we don't
# want to wreck for downstream chunks.
bm_storage <- basename(tempfile(fileext = "_storage"))
bm_project <- basename(tempfile(fileext = "_project"))
dir.create(here::here(bm_storage)); dir.create(here::here(bm_project))
system2("git", c("init", here::here(bm_project)), stdout = FALSE, stderr = FALSE)
setwd(here::here(bm_project))
dvs_init(storage_path = here::here(bm_storage), root_dir = here::here(bm_project))# A tibble: 1 × 4
compression metadata_folder_name backend_path backend_group
<chr> <lgl> <chr> <chr>
1 zstd NA /Users/elea/Documents/a2ai_github/dvs2-demo-repo/file774f2934d750_storage staff writeLines("x,y,z", here::here(bm_project, "tracked.csv"))
dvs_add(paths = "tracked.csv")# A tibble: 1 × 5
path outcome hash size stored_size
<chr> <chr> <chr> <bytes> <bytes>
1 tracked.csv copied 1357bc0161e229426dd1fba64a6adfd193f667632c54a5614ebde7370b700a5a 6 B 15 B# Remove the local copy and delete every blob from storage
file.remove(here::here(bm_project, "tracked.csv"))[1] TRUEblob_files <- list.files(here::here(bm_storage), recursive = TRUE, full.names = TRUE)
invisible(file.remove(blob_files[!grepl("audit", blob_files)]))
dvs_get(paths = "tracked.csv") |> print(width = Inf, n = Inf)# A tibble: 1 × 2
path error
<chr> <chr>
1 tracked.csv Storage file missing for hash: Hashes(blake3=1357bc0161e229426dd1fba64a6adfd193f667632c54a5614ebde7370b700a5a)Sys.setenv(BM_PROJECT = here::here(bm_project))cd "$BM_PROJECT"
dvs get tracked.csvError: tracked.csv - Storage file missing for hash: Hashes(blake3=1357bc0161e229426dd1fba64a6adfd193f667632c54a5614ebde7370b700a5a)
Error: Some files failed to get🔗Retrieved file does not match expected hash
The blob exists and decompresses successfully, but its content hashes to something other than the hash recorded in the meta file. Simulates content-level corruption of the blob.
# Fresh project + storage
hm_storage <- basename(tempfile(fileext = "_storage"))
hm_project <- basename(tempfile(fileext = "_project"))
dir.create(here::here(hm_storage)); dir.create(here::here(hm_project))
system2("git", c("init", here::here(hm_project)), stdout = FALSE, stderr = FALSE)
setwd(here::here(hm_project))
dvs_init(storage_path = here::here(hm_storage), root_dir = here::here(hm_project))# A tibble: 1 × 4
compression metadata_folder_name backend_path backend_group
<chr> <lgl> <chr> <chr>
1 zstd NA /Users/elea/Documents/a2ai_github/dvs2-demo-repo/file774f2b394d53_storage staff writeLines("original", here::here(hm_project, "tampered.csv"))
dvs_add(paths = "tampered.csv")# A tibble: 1 × 5
path outcome hash size stored_size
<chr> <chr> <chr> <bytes> <bytes>
1 tampered.csv copied 4618385b07b77df8055daff68db05c4e3c37151fc3306a3f2bdbeb1958419234 9 B 18 B# Replace the stored blob with zstd-compressed different content under the
# same filename (so the hash-keyed path resolves, but the decompressed bytes
# no longer match the recorded hash).
blob <- list.files(hm_storage, recursive = TRUE, full.names = TRUE)
blob <- blob[!grepl("audit", blob)]
Sys.chmod(blob, mode = "0644")
# zstd -q reads stdin and writes compressed bytes to stdout
system2("bash", c("-c",
sprintf("printf 'DIFFERENT BYTES' | zstd -q > %s",
shQuote(blob))))
# Remove local file so get actually goes to storage
file.remove(here::here(hm_project, "tampered.csv"))[1] TRUEdvs_get(paths = "tampered.csv") |> print(width = Inf, n = Inf)# A tibble: 1 × 3
path outcome size
<chr> <chr> <bytes>
1 tampered.csv copied 9 BSys.setenv(HM_PROJECT = here::here(hm_project))cd "$HM_PROJECT"
dvs get tampered.csv🔗Cleanup
setwd(here::here())
unlink(here::here(new_project), recursive = TRUE)
unlink(here::here(storage), recursive = TRUE)
for (d in c("bse_project", "bse_storage", "ump_project", "ump_storage",
"bm_project", "bm_storage", "hm_project", "hm_storage",
"outside", "outside2", "outside3")) {
if (exists(d)) unlink(here::here(get(d)), recursive = TRUE)
}