Skip to content

add_file_node discards the duplicate-path signal, so two nodes at one path silently alias #487

Closed

Description

@typeless

Symptom

add_file_node can register two different nodes under one path and silently keep only the last. Nothing reports it, and the losing node becomes unreachable by path while still existing in the graph.

Mechanism

add_file_node interns <parent path>/<name> into a PathId and registers it:

node.path_id = graph.paths.intern(parent_path, node.name);
graph.path_to_node.insert(to_underlying(node.path_id), id);

src/graph/dag.cpp:96-103. The return value is discarded.

SortedPairVec::insert (src/core/sorted_id_vec.cpp:179-185) is not an insert-or-fail - on an existing key it overwrites the value and returns false:

auto const pos = lower_bound(key);
if (pos < size_ && data_[pos].key == key) {
    data_[pos].value = value;
    return false;
}

So the signal that would name a duplicate path is produced and thrown away at the only call site that could act on it. Two nodes with the same parent and the same name leave path_to_node pointing at whichever was added second; the first is orphaned from every path lookup, including ensure_file_node.

Why it is worth closing

Path uniqueness is currently a convention every producer of a node cooperates in, not an invariant. Two live instances of the shared-namespace problem have been found in the last day:

Both were found by accident. A Result error - or at minimum a diagnostic - on a duplicate path_id would have surfaced each of them at the point of the collision rather than as a stale build days later, and would cover node kinds nobody has audited yet.

What I did not do

I did not construct a repro that reaches the duplicate-insert branch itself. #486 is a lookup collision (ensure_file_node returns the node already registered at that path) rather than a second insert, so it demonstrates the shared namespace but not this branch. The finding here is from reading the two functions above; treat the reachability of the duplicate insert as unverified.

Decisions to make

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions