{"record":{"id":"cddaac9fc6cdf521","repo":"weaviate/weaviate","slug":"find-and-connect-neighbors","errorCode":null,"errorMessage":"find and connect neighbors","messagePattern":"find and connect neighbors","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"adapters/repos/db/vector/hnsw/delete.go","lineNumber":668,"sourceCode":"\t\tvar e storobj.ErrNotFound\n\t\tif errors.As(err, &e) {\n\t\t\th.handleDeletedNode(e.DocID, \"reassignNeighbor\")\n\t\t\treturn true, nil\n\t\t} else {\n\t\t\t// not a typed error, we can recover from, return with err\n\t\t\treturn false, errors.Wrap(err, \"get neighbor vec\")\n\t\t}\n\t}\n\n\t// the new recursive implementation no longer needs an entrypoint, so we can\n\t// just pass this dummy value to make the neighborFinderConnector happy\n\tneighborNode.markAsMaintenance()\n\tdefer neighborNode.unmarkAsMaintenance()\n\n\tdummyEntrypoint := uint64(0)\n\tif err := h.reconnectNeighboursOf(ctx, neighborNode, dummyEntrypoint, neighborVec, compressorDistancer,\n\t\tneighborLevel, currentMaximumLayer, deleteList, processedIDs); err != nil {\n\t\treturn false, errors.Wrap(err, \"find and connect neighbors\")\n\t}\n\n\th.metrics.CleanedUp()\n\treturn true, nil\n}\n\nfunc connectionsPointTo(connections *packedconn.Connections, needles helpers.AllowList) bool {\n\t// Use CopyLayer with buffer reuse to avoid allocations per layer\n\tbuffer := make([]uint64, 0, 64)\n\n\tfor layer := uint8(0); layer < connections.Layers(); layer++ {\n\t\tbuffer = connections.CopyLayer(buffer, layer)\n\t\tfor _, pointer := range buffer {\n\t\t\tif needles.Contains(pointer) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}","sourceCodeStart":650,"sourceCodeEnd":686,"githubUrl":"https://github.com/weaviate/weaviate/blob/75aa4b6d11f8818305aafd4440b4e32794f7ca04/adapters/repos/db/vector/hnsw/delete.go#L650-L686","documentation":"Wrapped failure from reconnectNeighboursOf while re-linking a neighbor after a tombstoned node was removed from the HNSW graph. reassignNeighbor finds the affected neighbor, retrieves its vector, and asks the neighbor finder/connector to discover and connect new neighbors; any failure in that 'find and connect' step is wrapped with this message and aborts this node's reassignment for the current cleanup cycle.","triggerScenarios":"Tombstone cleanup calls reassignNeighbor; reconnectNeighboursOf fails — commonly due to a length-mismatch (ErrVectorLength) when comparing the neighbor's vector against candidates, store read failures while loading candidate nodes, or context cancellation.","commonSituations":"Mixed-dimension vectors in one collection after a schema/vectorizer change; heavy concurrent writes interfering with cleanup; context cancelled due to shutdown or timeout during a long cleanup pass.","solutions":["Unwrap to find the root cause; if it is ErrVectorLength, locate objects with wrong-dimension vectors and re-insert them with correct dimensions","Retry — cleanup is periodic and will retry failed reassignments","Check that the collection's vectorizer dimension matches all stored vectors","Ensure the process is not being shut down mid-cleanup (context cancellation); give cleanup time on a healthy node","If the graph remains inconsistent, trigger a full index rebuild"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Pre-validate vector dimensions for the neighbor before reconnect\nif len(neighborVec) != h.dim() {\n    return fmt.Errorf(\"neighbor vec dim %d != index dim %d\", len(neighborVec), h.dim())\n}","typeGuard":"var vecErr distancer.ErrVectorLength\nif errors.As(err, &vecErr) { /* dimension mismatch: fix data, not the graph */ }","tryCatchPattern":"err := h.reconnectNeighboursOf(ctx, node, dummy, vec, d, level, maxLayer, deleteList, processed)\nif err != nil {\n    if errors.Is(err, distancer.ErrVectorLength) { /* flag object for re-insert */ }\n    return false, fmt.Errorf(\"find and connect neighbors: %w\", err)\n}","preventionTips":["Keep one embedding model/dimension per collection; reindex after any model change","Let the periodic cleanup retry failed reassignments instead of panicking the worker","Respect context cancellation — don't kill the process mid-cleanup","Flag objects whose vectors fail dimension checks for deletion and re-insertion"],"tags":["hnsw","vector-index","tombstone-cleanup","go"],"backgroundTag":"hnsw-neighbor-reassignment-failed","analyzedSha":"75aa4b6d11f8818305aafd4440b4e32794f7ca04","analyzedAt":"2026-09-04T14:58:20.392Z","contentChangedAt":"2026-09-04T14:58:20.392Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}