Troubleshooting
Common issues and solutions
“Database is locked”
DuckDB uses file-level locking. Only one process can write at a time.
Symptoms: duckdb.IOException: Could not set lock on file
Solutions:
- Wait for the other process to finish (e.g., a running
getorscan). - Read-only commands (
check,list,resolve,stats,mk,coverage,duplicates) open the database in read-only mode and can run concurrently with a writer. - If no other process is running, the lock file may be stale. Delete
<db_path>.wal(the write-ahead log) and retry.
“No locally acquired metakernels”
You need to configure a mission and download a metakernel first.
spice-kernel-db mission add # set up a mission
spice-kernel-db browse MISSION # see what's available
spice-kernel-db get # download one interactively“Not found” when resolving a kernel
The kernel exists on disk but isn’t in the database yet.
# Re-index the directory containing the kernel
spice-kernel-db scan /path/to/kernelsspice-kernel-db get downloads everything again
By default, the tool skips files that match both the expected size and SHA-256 hash. If files were corrupted or partially downloaded, they’ll be re-downloaded. Use --force to bypass all skip checks.
Hash mismatch for <name>: computed X, expected Y during get/update
This was a download bug, fixed by writing each download to a temporary file and atomically replacing the destination (rather than open(dest, "wb"), which followed symlinks).
The cause: content-addressed deduplication leaves a symlink at an alias filename pointing to the shared canonical kernel. When the alias and its target later diverge upstream (e.g. a mission’s predict CK and a measured CK that were once byte-identical), re-downloading wrote the new bytes straight through the symlink into the shared file. Parallel downloads of several aliases pointing at one file would clobber each other, and the corruption surfaced as this hash-mismatch error (the integrity check correctly refusing the bad file).
If you hit this on an older version, recover by removing the affected files and re-fetching:
# Re-fetch the metakernels that touch the corrupted kernels
spice-kernel-db get <metakernel-url> --force
# Then re-run dedup so identical content is re-linked cleanly
spice-kernel-db dedupTo inspect which alias filenames share a physical file, look for cross-name symlinks in the kernel tree:
find <kernel-dir> -type l -exec sh -c \
'[ "$(basename "$1")" != "$(basename "$(readlink "$1")")" ] && echo "$1 -> $(readlink "$1")"' _ {} \;Metakernel paths don’t work with SpiceyPy
SPICE resolves PATH_VALUES relative to the current working directory, not the metakernel file location. The tool writes absolute PATH_VALUES in saved .tm files so they work from any directory.
If you see SPICE(NOSUCHFILE), check that the paths in the .tm file are absolute:
grep PATH_VALUES /path/to/your.tmSymlinks not supported (Windows)
On Windows, symlink creation requires Developer Mode or administrator privileges. If symlinks fail, the tool still downloads and registers files — only the symlinks are skipped. The metakernel may not work directly; use rewrite to create a local copy with correct paths.
SpiceyPy not found for coverage
The coverage command requires the optional spiceypy dependency:
pip install spice-kernel-db[spice]Or install SpiceyPy separately: pip install spiceypy
Corrupt config file
If config.toml is malformed, the tool prints an error and falls back to interactive setup. To fix manually:
# Show the config file location
spice-kernel-db config
# Re-run setup
spice-kernel-db config --setupOr edit ~/.config/spice-kernel-db/config.toml directly:
[database]
path = "~/.local/share/spice-kernel-db/kernels.duckdb"
[storage]
kernel_dir = "~/.local/share/spice-kernel-db/kernels"Stale entries after deleting files
If you’ve deleted kernel files from disk, the database still references them. Use prune to clean up:
# Preview what would be removed
spice-kernel-db prune
# Actually remove stale entries
spice-kernel-db prune --executeupdate fails with “Metakernel unreachable” / HTTP 404
NAIF (and ESA) routinely rotate older versioned metakernel snapshots into a former_versions/ subdirectory. Once that happens, the original URL stored in your local metakernel_registry returns 404, and spice-kernel-db update <mk> aborts with a red panel like:
╭─── Metakernel unreachable ───╮
│ The remote metakernel is no longer available.
│ URL: …/mk/juice_crema_5_1_…_v462_20260223_001.tm
│ Status: HTTP 404
│ NAIF (and ESA) routinely rotate old versioned snapshots into a
│ former_versions/ subdirectory, which makes the original URL 404.
╰──────────────────────────────╯
The exit code is 2 (distinct from generic LookupError’s 1), so scripts can detect this case specifically.
Fix it with prune --metakernels:
# Preview every dead registry row (HEAD-probes each source_url)
spice-kernel-db prune --metakernels
# Actually remove the dead rows
spice-kernel-db prune --metakernels --execute
# Also delete the on-disk .tm files (symlink trees are shared across
# metakernels in a mission and are never auto-deleted)
spice-kernel-db prune --metakernels --execute --delete-filesThe pruner treats 403, 404, and 410 as permanently dead; transient errors (timeouts, DNS failures, 5xx) deliberately don’t classify a row as dead, so going offline never causes accidental deletion.
If you just want to remove the one offending entry:
spice-kernel-db mk --remove juice_crema_5_1_…_v462_20260223_001.tmThen re-acquire the current version with browse → get.
Want to re-fetch the archived version instead? get automatically retries under former_versions/ when the live mk/ URL 404s, so:
spice-kernel-db get juice_crema_5_1_…_v462_20260223_001.tm --mission JUICE…will transparently download the archived metakernel and its referenced kernels (which ESA keeps live in kernels/{spk,fk,…}/ even after a .tm is rotated out). This is the recommended way to reproduce analyses against a specific older metakernel version.
verify reports unexpected statuses
After a get or rewrite, run spice-kernel-db verify <mk> to cross-check the rewritten metakernel against the DB. Common findings:
| Status | What it means | What to do |
|---|---|---|
DANGLING |
Symlink target moved or was deleted | Re-run get/update on this metakernel — the fixed _link_existing_kernels now repairs dangling links. |
HASH_MISMATCH (deep mode) |
File was modified after registration | prune --execute to drop the location, then re-get to fetch a clean copy. |
AMBIGUOUS |
Two non-superseded kernels rows share this filename |
Indicates the DB has two distinct files with the same name. Use find_by_filename in the Python API to see them, decide which to keep, and prune the other. |
UNREGISTERED |
File on disk but no kernels row |
Run scan <dir> on the directory containing it. |
BAD_PATH_VALUE |
PATH_VALUES not absolute |
Use get (which writes absolute paths) instead of editing .tm files by hand. |
TRAVERSAL |
A KERNELS_TO_LOAD entry escapes PATH_VALUES |
The metakernel is malformed or hostile — don’t furnsh it. |
Mission has no metakernel directory at the default path
mission add looks for a metakernel directory at {server_url}{MISSION}/kernels/mk/. Most NASA missions on NAIF do not have one — they publish individual per-type kernels (ck/, fk/, lsk/, pck/, sclk/, spk/) but no curated mission-wide .tm file. An empirical survey of NAIF in 2026 found:
- 8 missions with
kernels/mk/(mostly ESA-mirror entries — BEPICOLOMBO, EXOMARS2016, JUICE, LADEE, MEX, ROSETTA, SMART1, VEX) - 68 missions with no
mk/subdirectory anywhere on NAIF - 0 missions using alternative templates like
spice_kernels/mk/,data/spice/mk/, etc.
When the default path 404s, you have two productive options:
- Manual override — pass
--mk-dir-url <URL>tomission add. The URL is HEAD-checked but otherwise used as-is. Use this when you’ve found the metakernel directory yourself — typically on a PDS node such aspds-imaging.jpl.nasa.govorpds-smallbodies.astro.umd.edu. - planetarypy — for PDS-archived missions, the planetarypy library is the right tool. Install the optional bridge with
pip install spice-kernel-db[planetarypy]and pass--use-planetarypyonmission add. Full delegation is tracked in the issue tracker — the current stub prints a notice and falls back to normal discovery.
The curated mission_registry.toml exists as an extension point: if you confirm a working metakernel directory (anywhere on the web), please open a PR adding an entry so other users benefit:
["YOUR-MISSION"]
candidates = [
"{server}{m}/some/alt/path/mk/",
"https://some-pds-node.example/spice/your-mission/mk/",
]
planetarypy = false # set to true if planetarypy manages this missionRegistry entries can use absolute URLs (PDS nodes, mission websites) — the {server}/{m} placeholders are convenient but not required.