Skip to content

fix(api): GET /{index} 404s on an alias name instead of resolving it - #49

Closed
Vinz2168 wants to merge 1 commit into
xerj-org:mainfrom
Vinz2168:fix/get-index-alias-resolution
Closed

fix(api): GET /{index} 404s on an alias name instead of resolving it#49
Vinz2168 wants to merge 1 commit into
xerj-org:mainfrom
Vinz2168:fix/get-index-alias-resolution

Conversation

@Vinz2168

Copy link
Copy Markdown
Collaborator

Summary

GET /{index} (the Get Index API — returns aliases + mappings + settings) 404s when {index} is an alias name instead of resolving it to its backing physical index. Every other index-scoped endpoint (_search, _update, _alias, HEAD /{index}) resolves aliases correctly — this handler was the one place that shortcut alias resolution.

Real-world impact

This broke Kibana's own startup migration check. Kibana calls GET /.kibana at boot to see whether a prior migration already created and aliased .kibana_1. With this bug, xerj always reported .kibana as missing (even though .kibana_1 existed and the alias was correctly set — confirmed via GET /_alias/.kibana, GET /.kibana/_search, and POST /.kibana/_update/..., all of which worked fine). Kibana concluded no migration had happened, tried to freshly CREATE .kibana_1, collided with the physically existing index, and got stuck forever on "Another Kibana instance appears to be migrating the index. Waiting for that migration to complete." — the only workaround was deleting .kibana_1 entirely and letting Kibana re-migrate from scratch.

Root cause

get_index_inner's exact-name resolution checked literal membership in the physical index list (all.iter().any(|info| info.name == part)) before ever consulting alias metadata, so an alias name always fell into the "missing" branch and returned 404 immediately — alias resolution never got a chance to run. Every other handler in the codebase either calls engine.get_index() (which is alias-aware internally) or pre-expands the selector through engine.resolve_alias() before touching the physical index list.

Fix

Resolve each exact-name selector segment through engine.resolve_alias() before checking it against the physical index list, mirroring the pre-expansion pattern _search/_update/_field_caps already use. The resolved physical name is what's returned in the response — matching real Elasticsearch, which keys a Get Index API response by the concrete backing index rather than the alias, and matching what GET /_alias/{name} in this codebase already reports.

Test plan

  • cargo build --release -p xerj-api -p xerj-engine -p xerj-server
  • cargo fmt --check / cargo clippy --no-deps -- -D warnings — clean
  • Full ES-compat YAML conformance suite: 1360 passed, 0 failed, 3 skipped — no regressions
  • Manual verification: created an index + alias, confirmed GET /{alias} now returns 200 keyed by the backing index (was 404 before the fix)

🤖 Generated with Claude Code

Every other index-scoped endpoint (_search, _update, _alias, HEAD
/{index}) resolves an alias to its backing physical index before
acting -- get_index_inner was the one place that shortcut this by
checking literal membership in the physical index list first, so an
alias name always fell into the "missing" branch and 404'd before
alias resolution ever got a chance to run.

This broke Kibana's own startup migration check: Kibana calls
GET /.kibana at boot to see whether a prior migration already
created and aliased .kibana_1. With this bug xerj always reported
.kibana as missing, so Kibana concluded no migration had happened,
tried to freshly CREATE .kibana_1, collided with the physically
existing index, and got stuck forever on "Another Kibana instance
appears to be migrating the index" -- recurring across this session
and only ever worked around operationally by deleting .kibana_1 and
letting Kibana re-migrate from scratch.

Fix: resolve each exact-name selector segment through
engine.resolve_alias() before checking it against the physical index
list, mirroring the pre-expansion pattern _search/_update/_field_caps
already use. The resolved physical name is what's returned (matching
real ES, which keys a Get Index API response by the concrete backing
index, not the alias) and matches what GET /_alias/{name} in this
codebase already reports.

Verified: GET /{alias} now returns 200 keyed by the backing index
instead of 404. Full ES-compat YAML conformance suite: 1360 passed,
0 failed, 3 skipped -- no regressions.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016XJaZygeuRfZfUg2B8tPKU
@Vinz2168

Copy link
Copy Markdown
Collaborator Author

Duplicate of #30, opened earlier in the same session before a context compaction lost track of it. #30 is a superset of this fix — it also resolves aliases in the shared resolve_index_selector helper (not just get_index_inner) and additionally fixes alias persistence across restart, which this PR did not address. Closing this one in favor of #30.

@Vinz2168 Vinz2168 closed this Jul 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant