Summary
In workspace mode, get_health fails with Repository not found: <alias> for any repo whose workspace alias differs from its directory basename — even though list_repos advertises that exact alias and every other workspace tool (get_answer, get_dead_code, search_codebase) accepts it.
Version: repowise 0.31.0 (workspace mode, stdio transport).
Reproduce
Register two repos in a workspace where the alias does not equal the folder name:
| alias |
directory |
get_health(repo=<alias>) |
myrepo |
myrepo/ |
✅ works |
myrepo |
MyRepo/ (case differs) |
❌ Repository not found: myrepo |
nickname |
some-other-dir/ (aliased) |
❌ Repository not found: nickname |
list_repos() -> {"workspace": true, "repos": [{"alias": "nickname", ...}]}
get_health(repo="nickname", targets=["some/file.py"])
-> {"error": "Repository not found: nickname"}
get_answer(repo="nickname", question="...") -> works
get_dead_code(repo="nickname") -> works
I confirmed this across 6 workspace repos: the 3 whose alias exactly equals their directory basename work; the 3 where it differs (two by letter-case only, one aliased to an unrelated folder name) all fail. 6/6 consistent with the cause below.
Cause
There are two repo-resolution paths, and get_health uses the one that is not alias-aware.
server/mcp_server/_helpers.py:55 — _get_repo() resolves only by local_path, then primary-key ID, then Repository.name:
async def _get_repo(session: AsyncSession, repo: str | None = None) -> Repository:
"""Resolve a repository — by path, by ID, or return the first one."""
if repo:
# Try by path
result = await session.execute(select(Repository).where(Repository.local_path == repo))
...
# Try by ID
obj = await session.get(Repository, repo)
...
# Try by name
result = await session.execute(select(Repository).where(Repository.name == repo))
...
raise LookupError(f"Repository not found: {repo}")
It never consults the workspace alias registry. Repository.name is the directory basename, so a workspace alias only resolves here by coincidence — when the alias happens to equal the basename. The match is also case-sensitive, so an alias differing only in case fails.
The alias-aware path is core/workspace/registry.py:110 (resolve()), which validates against the workspace config and raises a different error:
Unknown repo 'X'. Available: ['a', 'b', ...]
Two distinct errors (worth noting for anyone debugging this)
These look similar but mean opposite things, which makes the bug confusing to diagnose:
Unknown repo 'X'. Available: [...] — alias resolution ran and X is genuinely not a registered alias.
Repository not found: X — the alias IS valid and resolved fine; _get_repo then failed to find it by name/path.
So Repository not found on an alias that list_repos just returned is always this bug.
Suggested fix
Have _get_repo() consult the workspace registry first (mapping alias → RepoContext/local_path) before falling back to path/ID/name lookup, so alias resolution is consistent across every workspace-aware tool. A case-insensitive comparison on the name fallback would additionally fix the case-only mismatches.
Workaround
None from the tool surface — passing the directory basename instead of the alias fails earlier in registry.resolve() with Unknown repo, since the basename is not a registered alias. Affected repos currently have no way to call get_health at all.
Related: #435
#435 ("Chat MCP tools fail with 'Repository not found' for non-primary repos in workspace mode") lands on the same final line (_helpers.py:72) and the same underlying weakness — _get_repo is not workspace/alias-aware. But this report is a different surface and a different trigger, so I've filed it separately rather than commenting there:
If the fix proposed in #435 (thread a per-request RepoContext instead of mutating MCP module globals) is applied to the shared _get_repo resolution rather than only the chat path, it would likely close both.
Summary
In workspace mode,
get_healthfails withRepository not found: <alias>for any repo whose workspace alias differs from its directory basename — even thoughlist_reposadvertises that exact alias and every other workspace tool (get_answer,get_dead_code,search_codebase) accepts it.Version: repowise 0.31.0 (workspace mode, stdio transport).
Reproduce
Register two repos in a workspace where the alias does not equal the folder name:
get_health(repo=<alias>)myrepomyrepo/myrepoMyRepo/(case differs)Repository not found: myreponicknamesome-other-dir/(aliased)Repository not found: nicknameI confirmed this across 6 workspace repos: the 3 whose alias exactly equals their directory basename work; the 3 where it differs (two by letter-case only, one aliased to an unrelated folder name) all fail. 6/6 consistent with the cause below.
Cause
There are two repo-resolution paths, and
get_healthuses the one that is not alias-aware.server/mcp_server/_helpers.py:55—_get_repo()resolves only bylocal_path, then primary-key ID, thenRepository.name:It never consults the workspace alias registry.
Repository.nameis the directory basename, so a workspace alias only resolves here by coincidence — when the alias happens to equal the basename. The match is also case-sensitive, so an alias differing only in case fails.The alias-aware path is
core/workspace/registry.py:110(resolve()), which validates against the workspace config and raises a different error:Two distinct errors (worth noting for anyone debugging this)
These look similar but mean opposite things, which makes the bug confusing to diagnose:
Unknown repo 'X'. Available: [...]— alias resolution ran and X is genuinely not a registered alias.Repository not found: X— the alias IS valid and resolved fine;_get_repothen failed to find it by name/path.So
Repository not foundon an alias thatlist_reposjust returned is always this bug.Suggested fix
Have
_get_repo()consult the workspace registry first (mapping alias →RepoContext/local_path) before falling back to path/ID/name lookup, so alias resolution is consistent across every workspace-aware tool. A case-insensitive comparison on thenamefallback would additionally fix the case-only mismatches.Workaround
None from the tool surface — passing the directory basename instead of the alias fails earlier in
registry.resolve()withUnknown repo, since the basename is not a registered alias. Affected repos currently have no way to callget_healthat all.Related: #435
#435 ("Chat MCP tools fail with 'Repository not found' for non-primary repos in workspace mode") lands on the same final line (
_helpers.py:72) and the same underlying weakness —_get_repois not workspace/alias-aware. But this report is a different surface and a different trigger, so I've filed it separately rather than commenting there:chat_tools.execute_tool+ module-global_statewired to the primary repo). This is the plain stdio MCP transport, where that global-state path isn't involved.Repository.name, case-sensitive.get_healthis affected on stdio;get_answer/get_dead_code/search_codebaseresolve the same aliases correctly on the same repos, because they go throughregistry.resolve().If the fix proposed in #435 (thread a per-request
RepoContextinstead of mutating MCP module globals) is applied to the shared_get_reporesolution rather than only the chat path, it would likely close both.