Skip to content

Cluster tab deep links unreliable: hash routing collision between route path and tab state #418

Description

@mavcode

Summary

Deep-linking to the Cluster tab on a collection page using a URL such as:

https://<host>/dashboard#/collections/<name>#cluster

works intermittently. When it fails, the UI/API returns 404 because the collection name is parsed as <name>#cluster instead of <name>.

Additionally, clicking the Cluster tab calls navigate('#cluster'), which can replace the route hash and drop the /collections/<name> path, breaking refresh and shareable URLs.

Steps to reproduce

  1. Deploy Qdrant with API key authentication enabled.
  2. Open the Web UI and configure a valid API key in settings.
  3. Ensure a collection exists (e.g. sample).
  4. Navigate directly to: https://<host>/dashboard#/collections/sample#cluster
  5. Observe: sometimes the Cluster tab loads; sometimes the page shows 404 / "collection not found".
  6. Alternatively:
    • Open https://<host>/dashboard#/collections/sample
    • Click the Cluster tab
    • Note the browser URL changes to https://<host>/dashboard#cluster (route path lost)
    • Refresh the page → collection context is lost

Expected behavior

  • #/collections/sample#cluster should consistently open the sample collection with the Cluster tab selected.
  • Clicking a collection sub-tab should preserve the collection route in the URL so refresh and deep links work reliably.

Actual behavior

Intermittent 404 on deep link

The browser treats everything after the first # as a single fragment: /collections/sample#cluster. Depending on parse timing / router behavior, :collectionName may become sample#cluster. The API then returns:

{
  "status": {
    "error": "Not found: Collection `sample#cluster` doesn't exist!"
  }
}

Tab navigation breaks route

In src/pages/Collection.jsx:

const [currentTab, setCurrentTab] = useState(location.hash.slice(1) || 'points');

const handleTabChange = (event, newValue) => {
  setCurrentTab(newValue);
  navigate(`#${newValue}`);
};

Calling navigate('#cluster') under HashRouter replaces the hash with #cluster, removing #/collections/<name> from the URL.

Root cause (proposed)

HashRouter uses the URL hash for route paths (#/collections/:collectionName), while Collection.jsx also uses the hash for tab state (#cluster, #points, etc.). Browsers only support one # fragment per URL, so route and tab compete for the same mechanism.

Suggested fix

One of:

  1. Query parameter for tabs — e.g. #/collections/sample?tab=cluster
  2. Nested route segments — e.g. #/collections/sample/cluster (add tab routes in routes.jsx)
  3. In-memory tab state only — don't call navigate('#cluster'); update tab state without mutating the route hash

Environment

Item Value
Qdrant version v1.18.2
Web UI Bundled with Qdrant (served at /dashboard)
Router HashRouter (src/index.jsx)
Auth API key required; key via Web UI settings (localStorage)

API endpoints used by Cluster tab work consistently when called directly (GET /collections/sample, GET /collections/sample/cluster, GET /cluster → all 200).

Workaround

  1. Open https://<host>/dashboard#/collections/<name> (no #cluster suffix).
  2. Click the Cluster tab in the UI.
  3. Avoid refreshing after the URL collapses to #cluster only.

References

  • src/pages/Collection.jsx
  • src/components/Collections/ClusterMonitor/ClusterMonitor.jsx
  • src/index.jsx

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