Skip to content

qlever rebuild-index always fails on the default path: CLI copies Qleverfile into the target dir, but the server rejects a non-empty target #313

Description

@mpagni12

Hope this helps improve qlever CLI

Marco


qlever rebuild-index always fails on the default path: CLI copies Qleverfile into the target dir, but the server rejects a non-empty target

Repo: ad-freiburg/qlever-control (CLI). Feature tracked in engine issue #2621.

Summary

qlever rebuild-index, invoked exactly as documented (from the dataset directory,
no extra flags), fails deterministically with:

Rebuilding the index failed: The directory "rebuild.<ts>.tmp" already exists and is not empty

Root cause is a CLI/server contract mismatch. The CLI first creates the new-index
target directory and copies the Qleverfile into it, and only then asks the server
to build the new index into that same directory. The server-side cmd=rebuild-index
refuses to build into a non-empty directory, so the CLI's own cp -a Qleverfile
step guarantees the failure.

This is not macOS-specific and not an engine problem: the engine's
cmd=rebuild-index works and hot-swaps correctly when the target directory is empty
(shown below). The bug is purely in the CLI wrapper's ordering.

Environment

  • qlever CLI: 0.5.50 (installed via uv tool)
  • Image: adfreiburg/qlever@sha256:6ba10ddf71e7d06ece47eaf927339213c64c342f9ac719ab6c97968d81aef186
    (server git hash 634ebb2, compiled 2026-08-03)
  • SYSTEM = docker; Docker 29.5.2; host macOS 26.6 (mechanism is OS-independent)

Minimal reproduction

data.ttl:

@prefix ex: <http://example.org/> .
ex:a ex:p ex:b .

Qleverfile:

[data]
NAME          = ps3
DESCRIPTION   = rebuild-index repro
[index]
INPUT_FILES     = *.ttl
CAT_INPUT_FILES = cat ${INPUT_FILES}
[server]
PORT         = 7035
HOST_NAME    = localhost
ACCESS_TOKEN = tok
[runtime]
SYSTEM = docker
IMAGE  = docker.io/adfreiburg/qlever:latest

Steps (pristine directory, first-ever rebuild - no leftover rebuild.*/previous.* dirs):

qlever index
qlever start
# make the rebuild non-trivial (optional; fails the same with or without updates):
curl -s localhost:7035 \
  --data-urlencode 'update=INSERT DATA { <http://example.org/x> <http://example.org/p> <http://example.org/y> }' \
  --data-urlencode 'access-token=tok'
qlever rebuild-index

Observed

Command: rebuild-index
mkdir -p rebuild.<ts>.tmp && cp -a Qleverfile rebuild.<ts>.tmp
curl -s -w '\n%{http_code}' localhost:7035 -d cmd=rebuild-index -d index-name=rebuild.<ts>.tmp/ps3 -d access-token=tok
mkdir -p previous.<ts>.ORIGINAL && mv ps3.* Qleverfile previous.<ts>.ORIGINAL && mv rebuild.<ts>.tmp/* . && rmdir rebuild.<ts>.tmp
Rebuilding the index failed: The directory "rebuild.<ts>.tmp" already exists and is not empty

The rebuild never runs; the server keeps serving the old index (non-destructive).

Isolation (empty vs non-empty target dir)

Talking to the server directly, the only difference is whether the target
directory is empty:

# EMPTY target dir -> success
mkdir dirB
curl -s -w '\nHTTP=%{http_code}\n' localhost:7035 -d cmd=rebuild-index -d index-name=dirB/ps3 -d access-token=tok
# => {"message":"Index successfully rebuilt and swapped in", ...}  HTTP=200

# target dir containing only Qleverfile (what the CLI creates) -> failure
mkdir dirA && cp Qleverfile dirA
curl -s -w '\nHTTP=%{http_code}\n' localhost:7035 -d cmd=rebuild-index -d index-name=dirA/ps3 -d access-token=tok
# => The directory "dirA" already exists and is not empty   HTTP=400

When it succeeds, the swap is seamless: the server container is not restarted
and continues serving (count preserved across the swap).

Expected

qlever rebuild-index should succeed on the default path.

Suggested fix

Either:

  • copy the Qleverfile into the new-index directory after the server has built
    the index there (not before), or
  • have the server-side empty-directory check ignore a lone Qleverfile.

Secondary observations (context, not necessarily separate reports)

  1. Repeated rebuilds collide on the auto-named previous.<ts> directory:
    The directory "previous.<ts>" already exists and is not empty (HTTP 400) when
    rebuilds happen in quick succession / the timestamp basis does not advance.
  2. rebuild-index --new-index-dir <dir>: the validation step starts a throwaway
    container (-u $(id -u):$(id -g)) that reports
    Could not open ... .meta-data.json ... permissions are insufficient - this one
    may be a macOS Docker Desktop uid/bind-mount quirk and needs a Linux cross-check.
  3. Docs drift: the engine returns "...rebuilt and swapped in" and does hot-swap
    without a restart, but https://docs.qlever.dev/rebuild-index/ still states
    "There is currently no way to transition from the old index to the new index
    without any interruption."

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions