Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion src/basic_memory/api/v2/routers/project_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,20 @@ async def resolve_project_identifier(
)

if not project:
raise HTTPException(status_code=404, detail=f"Project not found: '{data.identifier}'")
detail = f"Project not found: '{data.identifier}'"
# Trigger: resolution missed and the projects table is empty.
# Why: a fresh install bootstraps config.json's default project before any
# reconciliation has created database rows (the one-shot CLI never runs
# the server lifespan), so the first read fails on the configured
# default and the bare not-found message reads as a broken install
# rather than a missing first-run step (#974 follow-up).
# Outcome: the error names the setup command instead.
if not await project_repository.find_all(limit=1, use_load_options=False):
detail = (
f"{detail}. No projects are set up yet — run "
"'basic-memory project add <name> <path>' to create one."
)
raise HTTPException(status_code=404, detail=detail)

return ProjectResolveResponse(
external_id=project.external_id,
Expand Down
35 changes: 35 additions & 0 deletions tests/api/v2/test_project_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,41 @@ async def test_resolve_project_not_found(client: AsyncClient, v2_projects_url):
assert "not found" in response.json()["detail"].lower()


@pytest.mark.asyncio
async def test_resolve_project_not_found_fresh_install_names_setup_command(
client: AsyncClient, v2_projects_url, project_repository
):
"""#974 follow-up: a fresh install fails its first read with a bare not-found.

config.json bootstraps a "main" default before any reconciliation has created
database rows (the one-shot CLI never runs the server lifespan), so resolving
the configured default 404s. With an empty projects table the error must point
at first-run setup instead of reading like a broken install.
"""
for project in await project_repository.find_all():
await project_repository.delete(project.id)

response = await client.post(f"{v2_projects_url}/resolve", json={"identifier": "main"})

assert response.status_code == 404
detail = response.json()["detail"]
assert detail.startswith("Project not found: 'main'")
assert "basic-memory project add" in detail


@pytest.mark.asyncio
async def test_resolve_project_not_found_with_projects_keeps_plain_message(
client: AsyncClient, test_project: Project, v2_projects_url
):
"""A miss against a populated projects table stays a plain not-found."""
response = await client.post(
f"{v2_projects_url}/resolve", json={"identifier": "nonexistent-project"}
)

assert response.status_code == 404
assert response.json()["detail"] == "Project not found: 'nonexistent-project'"


@pytest.mark.asyncio
async def test_resolve_project_empty_identifier(client: AsyncClient, v2_projects_url):
"""Test resolving with empty identifier returns 422."""
Expand Down
Loading