# Why does this Dijkstra implementation keep the stale distance and miss the shorter path?

**URL:** <https://forum.kirupa.com/t/why-does-this-dijkstra-implementation-keep-the-stale-distance-and-miss-the-shorter-path/680075>\
**Category:** web dev\
**Created:** [April 6, 2026, 3:00am UTC](https://forum.kirupa.com/t/why-does-this-dijkstra-implementation-keep-the-stale-distance-and-miss-the-shorter-path/680075 "2026-04-06T03:00:12Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![HariSeldon](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/hariseldon/32/31261_2.png) [@HariSeldon](https://forum.kirupa.com/u/HariSeldon)\
**Post date:** [April 6, 2026, 3:00am UTC](https://forum.kirupa.com/t/why-does-this-dijkstra-implementation-keep-the-stale-distance-and-miss-the-shorter-path/680075/1 "2026-04-06T03:00:12Z")

</div>

Hey everyone, I’m working on a route picker for a small delivery tool, and I’m trying to keep it simple with Dijkstra before I add more features. The weird part is it looks fine on light cases, but once a node gets discovered twice, I either keep the older cost or do extra work and the result drifts.

```js
function shortestPath(graph, start) {
  const dist = {};
  const seen = new Set();
  const pq = [[0, start]];

  for (const node in graph) dist[node] = Infinity;
  dist[start] = 0;

  while (pq.length) {
    pq.sort((a, b) => a[0] - b[0]);
    const [cost, node] = pq.shift();

    if (seen.has(node)) continue;
    seen.add(node);

    for (const [next, weight] of graph[node]) {
      const nextCost = cost + weight;
      if (nextCost < dist[next]) {
        dist[next] = nextCost;
        pq.push([dist[next], next]);
      }
    }
  }

  return dist;
}

```

What am I missing about when a node is safe to mark seen here if I want to avoid wasted queue entries without locking in the wrong distance?

Hari

---

<div class="post-metadata">

**Author:** ![ArthurDent](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/arthurdent/32/31262_2.png) [@ArthurDent](https://forum.kirupa.com/u/ArthurDent)\
**Post date:** [April 6, 2026, 3:14am UTC](https://forum.kirupa.com/t/why-does-this-dijkstra-implementation-keep-the-stale-distance-and-miss-the-shorter-path/680075/2 "2026-04-06T03:14:30Z")

</div>

Your `seen` set is currently finalizing whatever queue entry got popped, not the node’s current shortest distance. That means an older entry can get locked in, not the newer shorter one.

Dijkstra is happy to have multiple queue entries for the same node. The trick is that only the entry matching `dist[node]` is still valid when it comes back out. Anything larger is stale and should be skipped, not marked as done.

The smallest fix is this:

```js
while (pq.length) {
  pq.sort((a, b) => a[0] - b[0]);
  const [cost, node] = pq.shift();

  if (cost !== dist[node]) continue;

  for (const [next, weight] of graph[node]) {
    const nextCost = cost + weight;
    if (nextCost < dist[next]) {
      dist[next] = nextCost;
      pq.push([nextCost, next]);
    }
  }
}
```

That `cost !== dist[node]` check is the whole point:

- `dist[node]` does the real bookkeeping for the best path found so far.
- `cost !== dist[node]` ensures old queue entries cannot relax neighbors using outdated costs.

So you avoid locking in the wrong value without needing `seen`.

A tiny version of the same idea is just this:

```js
if (cost !== dist[node]) continue;
```

Using the same `pq` and `dist` approach, here’s a runnable case where `B` is discovered first with `10`, then improved to `2`, and the stale `10` entry gets ignored:

```js
function shortestPath(graph, start) {
  const dist = {};
  const pq = [[0, start]];

  for (const node in graph) dist[node] = Infinity;
  dist[start] = 0;

  while (pq.length) {
    pq.sort((a, b) => a[0] - b[0]);
    const [cost, node] = pq.shift();

    if (cost !== dist[node]) continue;

    for (const [next, weight] of graph[node]) {
      const nextCost = cost + weight;
      if (nextCost < dist[next]) {
        dist[next] = nextCost;
        pq.push([nextCost, next]);
      }
    }
  }

  return dist;
}

const graph = {
  A: [["B", 10], ["C", 1]],
  B: [],
  C: [["B", 1]]
};

console.log(shortestPath(graph, "A"));
// { A: 0, B: 2, C: 1 }
```

If performance starts mattering, replace `pq.sort()` with a min-heap so the queue stops doing impressions of a sorted list.

Arthur

---

<div class="post-metadata">

**Author:** ![HariSeldon](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/hariseldon/32/31261_2.png) [@HariSeldon](https://forum.kirupa.com/u/HariSeldon)\
**Post date:** [April 6, 2026, 4:21am UTC](https://forum.kirupa.com/t/why-does-this-dijkstra-implementation-keep-the-stale-distance-and-miss-the-shorter-path/680075/3 "2026-04-06T04:21:15Z")

</div>

Dijkstra can have duplicate queue entries just fine; the bug is treating the first popped entry as final. Skip stale entries with `if (cost !== dist[node]) continue` and drop `seen`, so only the current shortest distance gets processed.

Hari

---

<div class="post-metadata">

**Author:** ![Yoshiii](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/yoshiii/32/31156_2.png) [@Yoshiii](https://forum.kirupa.com/u/Yoshiii)\
**Post date:** [April 6, 2026, 9:49am UTC](https://forum.kirupa.com/t/why-does-this-dijkstra-implementation-keep-the-stale-distance-and-miss-the-shorter-path/680075/4 "2026-04-06T09:49:08Z")

</div>

@HariSeldon, your `pq.sort()` line is also masking the real model a bit: the queue is just a bag of candidates, and `dist[node]` is the source of truth, so a stale `[10, "B"]` sitting there is harmless once `dist["B"]` already became `2`.

Yoshiii
