Name the remedy for a missing configured database and list the databases an unknown name could have meant

A configured or default database that does not exist raises
SourceUnavailableError with the way to create it and without its location.
An unknown --db-name lists the databases there are, configured or default.
The CHANGELOG records Store.db_path, SingleDatabaseSession(ref, config),
the empty lancedb.uri migration and the error-type change.
This commit is contained in:
Yiorgis Gozadinos 2026-09-03 15:26:42 +03:00
parent 3814b2bc78
commit 187946a7ae
No known key found for this signature in database
8 changed files with 47 additions and 15 deletions

View file

@ -5,7 +5,8 @@
### Removed
- `lancedb.uri`. Write `lancedb.databases: {NAME: <location>}`; a config carrying
`uri` fails to load with that message.
`uri` fails to load with that message. Configurations generated by
`init-config` through 0.81 carry `uri: ""` and must drop the key.
- `HAIKU_RAG_DB`. Capabilities cover the databases the configuration places, or
the `db_path` argument.
- `DB_PATH` in the `app/` backend and `examples/custom_agent_agui.py`. Both load
@ -33,6 +34,12 @@
- `DatabaseRef(name, location, given)` replaces `DatabaseRef(name, uri, db_path)`;
`DatabaseScope.at(path)` added; `locate_database` returns `Path | str`.
`IngesterApp(config, scope)` takes a resolved scope in place of `db_path`.
`SingleDatabaseSession(ref, config)` replaces `SingleDatabaseSession(db_path,
config, source=)`. `Store.db_path` is `None` for a database behind a URI.
- Opening a configured or default database that does not exist raises
`SourceUnavailableError` naming the database and the remedy (`haiku-rag init`
or `create=True`), where the default database raised `FileNotFoundError` with
its path. A database given as a path still raises `FileNotFoundError`.
- `Store(location, config)`, `connect_lancedb(location, config)`,
`gather_database_info(location, config)` and `run_doctor(config, location, ...)`
take the database location, a path or a URI. `ConnectionMode.of(location)`

View file

@ -110,7 +110,7 @@ async with HaikuRAG(create=True) as client:
The [default location](index.md#configuration-file-locations) is platform-specific (e.g., `~/Library/Application Support/haiku.rag/` on macOS).
Opening a nonexistent local database given as a path raises `FileNotFoundError`, naming the path. This prevents accidental database creation from typos or misconfigured paths. A database placed by `lancedb.databases` raises `SourceUnavailableError` instead, naming the database and not its location.
Opening a nonexistent local database given as a path raises `FileNotFoundError`, naming the path. This prevents accidental database creation from typos or misconfigured paths. A configured or default database raises `SourceUnavailableError` instead, naming the database and not its location.
## Remote Storage

View file

@ -27,7 +27,7 @@ async with HaikuRAG("path/to/database.lancedb", read_only=True) as client:
`async with` is the lifecycle. A caller that owns the client some other way releases it with `await client.aclose()`, which does the same work for every client shape. `client.close()` closes the connection to one database and nothing else, since draining the background vacuum and releasing the embedder and reranker are awaitable; it refuses a client covering several.
!!! note
Databases must be explicitly created with `create=True` or via `haiku-rag init` before use. Opening a nonexistent local database given as a path raises `FileNotFoundError`, naming the path; one placed by `lancedb.databases` raises `SourceUnavailableError`, which names the database rather than its location. A path beside a configured `lancedb.databases` raises `AmbiguousDatabaseError`.
Databases must be explicitly created with `create=True` or via `haiku-rag init` before use. Opening a nonexistent local database given as a path raises `FileNotFoundError`, naming the path; a configured or default database raises `SourceUnavailableError`, which names the database rather than its location. A path beside a configured `lancedb.databases` raises `AmbiguousDatabaseError`.
!!! note
Read-only mode is useful for safely accessing databases without risk of modification. It blocks all write operations and downgrades an embedding provider/name mismatch to a warning instead of raising `ConfigMismatchError`.

View file

@ -126,7 +126,7 @@ class DatabaseScope:
if database_name is not None:
if database_name not in declared:
raise UnknownDatabaseError(
f"unknown database {database_name!r}; lancedb.databases names "
f"unknown database {database_name!r}; the databases are "
f"{', '.join(sorted(declared))}"
)
return cls(

View file

@ -113,13 +113,15 @@ class SingleDatabaseSession:
# A path the caller gave may be named: the caller knows it already.
if self.ref.given:
raise
failure = type(error).__name__
failure = (
"does not exist; create it with `haiku-rag init` or `create=True`"
if isinstance(error, FileNotFoundError)
else f"could not be opened: {type(error).__name__}"
)
if failure is not None:
# Raised outside the handler: the exception carries neither a cause
# nor a location-bearing context.
raise SourceUnavailableError(
f"database {self.source!r} could not be opened: {failure}"
)
raise SourceUnavailableError(f"database {self.source!r} {failure}")
self.document_repository = DocumentRepository(self.store)
self.chunk_repository = ChunkRepository(self.store)
self.document_item_repository = DocumentItemRepository(self.store)

View file

@ -557,12 +557,6 @@ class TestPlacingTheIngesterDatabase:
with pytest.raises(typer.BadParameter, match="no name"):
_scope_for(Path("/"))
def test_a_python_caller_cannot_pass_a_path(self):
from haiku.rag.ingester.app import IngesterApp
with pytest.raises(TypeError):
IngesterApp(config=AppConfig(), db_path="/db/other.lancedb") # type: ignore[call-arg] # ty: ignore[unknown-argument]
def test_one_configured_database_is_accepted(self, tmp_path):
"""A one-entry mapping names which database to write."""
config = AppConfig(

View file

@ -474,6 +474,25 @@ class TestFailureNaming:
assert str(tmp_path) not in str(caught.value)
assert caught.value.__cause__ is None
@pytest.mark.asyncio
async def test_a_missing_default_database_names_the_remedy(self, tmp_path):
"""The location stays out of the message; the way to create the
database does not."""
from haiku.rag.config.models import AppConfig, StorageConfig
config = AppConfig(storage=StorageConfig(data_dir=tmp_path / "empty"))
with pytest.raises(SourceUnavailableError) as caught:
async with HaikuRAG(config=config):
pass
message = str(caught.value)
assert "database 'haiku.rag' does not exist" in message
assert "haiku-rag init" in message
assert "create=True" in message
assert str(tmp_path) not in message
assert caught.value.__cause__ is None
@pytest.mark.asyncio
async def test_a_database_given_as_a_path_keeps_its_error(self, tmp_path):
"""The caller gave the path, so the error may name it."""

View file

@ -54,11 +54,21 @@ class TestResolution:
assert scope.names == ("beta",)
def test_an_unknown_name_is_refused(self):
"""The message lists the databases there are, configured or default."""
config = _config(databases={"alpha": "/data/alpha.lancedb"})
with pytest.raises(UnknownDatabaseError, match="unknown database 'nope'"):
with pytest.raises(
UnknownDatabaseError,
match="unknown database 'nope'.*the databases are alpha",
):
DatabaseScope.resolve(config, database_name="nope")
with pytest.raises(
UnknownDatabaseError,
match="unknown database 'nope'.*the databases are haiku.rag",
):
DatabaseScope.resolve(_config(), database_name="nope")
def test_no_selector_covers_the_configured_set_in_order(self):
config = _config(
databases={"beta": "/data/b.lancedb", "alpha": "/data/a.lancedb"}