What happened
ProbeView discovers and deduplicates Synthetic Monitoring checks using the (job, instance) pair, but generates the public check ID from slugify(job) alone.
Grafana defines a check's identity as the combination of Job name + Target, so two valid checks can share a job name while targeting different URLs. Those checks receive the same ProbeView ID. Distinct job names can also collide after slug normalization, for example Public API and Public-API.
The duplicate ID is then used as:
- the React list key on the overview;
- the
/site/<id> detail URL; and
- the lookup key in
getSiteHistory(), which returns the first matching check.
As a result, overview rows can have duplicate keys and URLs, and at least one affected check has no independently addressable detail page.
What you expected
Every discovered (job, instance) check should have a unique, stable public ID and detail route, even when job names are duplicated or normalize to the same slug.
Existing non-colliding detail URLs should remain compatible where practical.
Steps to reproduce
- Configure two Grafana Synthetic Monitoring checks with the same Job name and different Targets, or with names such as
Public API and Public-API.
- Load the ProbeView overview.
- Observe that both checks receive the same
/site/<id> link and React key.
- Open that detail URL and observe that
checks.find((c) => c.id === id) always selects the first matching check.
Environment
- App version / commit:
7ad4ff8
- Node version:
v24.18.0
- Running mode: [x] live (Grafana) [ ] sample data (
MOCK=1 / no creds)
- Host: [x] local [ ] Vercel [ ] other (specify)
Diagnostics
Not applicable. The collision follows from check discovery and route construction and does not depend on a failed Grafana query.
Relevant implementation:
lib/synthetics.ts: deduplication uses (job, instance), while id uses slugify(job).
components/Overview.tsx: c.id is used for the list key and detail URL.
lib/synthetics.ts: detail lookup uses the first check matching id.
Grafana documents Job name + Target as the check identifier:
https://grafana.com/docs/grafana-cloud/testing/synthetic-monitoring/create-checks/checks/
Logs / screenshots
No runtime error is required to trigger the bug.
Acceptance criteria
What happened
ProbeView discovers and deduplicates Synthetic Monitoring checks using the
(job, instance)pair, but generates the public check ID fromslugify(job)alone.Grafana defines a check's identity as the combination of Job name + Target, so two valid checks can share a job name while targeting different URLs. Those checks receive the same ProbeView ID. Distinct job names can also collide after slug normalization, for example
Public APIandPublic-API.The duplicate ID is then used as:
/site/<id>detail URL; andgetSiteHistory(), which returns the first matching check.As a result, overview rows can have duplicate keys and URLs, and at least one affected check has no independently addressable detail page.
What you expected
Every discovered
(job, instance)check should have a unique, stable public ID and detail route, even when job names are duplicated or normalize to the same slug.Existing non-colliding detail URLs should remain compatible where practical.
Steps to reproduce
Public APIandPublic-API./site/<id>link and React key.checks.find((c) => c.id === id)always selects the first matching check.Environment
7ad4ff8v24.18.0MOCK=1/ no creds)Diagnostics
Not applicable. The collision follows from check discovery and route construction and does not depend on a failed Grafana query.
Relevant implementation:
lib/synthetics.ts: deduplication uses(job, instance), whileidusesslugify(job).components/Overview.tsx:c.idis used for the list key and detail URL.lib/synthetics.ts: detail lookup uses the first check matchingid.Grafana documents Job name + Target as the check identifier:
https://grafana.com/docs/grafana-cloud/testing/synthetic-monitoring/create-checks/checks/
Logs / screenshots
No runtime error is required to trigger the bug.
Acceptance criteria
(job, instance)pair receives a unique, deterministic ID.