abhigyanpatwari/GitNexus · warning

Structural relationship count failed; the graph-write-collap

Error message

Structural relationship count failed; the graph-write-collapse check will have no structural measurement from this run.

What it means

Post-load validation could not count structural relationships (the MATCH ()-[r:REL]->() WHERE NOT r.type IN [excluded] count over the rel table failed). By contract the count stays undefined rather than becoming a misleading 0 — a zero would read as a total wipeout to the graph-write-collapse guard. The failure reason is carried on the result as structuralEdgesError and logged here, so 'guard could not measure' is distinguishable from 'guard ran and found nothing'.

Source

Thrown at gitnexus/src/core/lbug/lbug-adapter.ts:1948

  let structuralEdges: number | undefined;
  let structuralEdgesError: string | undefined;
  try {
    const excluded = [...PDG_EDGE_TYPES].map((t) => `'${t}'`).join(', ');
    structuralEdges = await withConnLock(async () => {
      const queryResult = await c.query(
        `MATCH ()-[r:${REL_TABLE_NAME}]->() WHERE NOT r.type IN [${excluded}] RETURN count(r) AS cnt`,
      );
      const rows = await readQueryRows(queryResult);
      return rows.length > 0 ? Number(rows[0]?.cnt ?? rows[0]?.[0] ?? 0) : 0;
    });
  } catch (err) {
    // Same contract as `edges`: leave undefined rather than report a zero the
    // collapse check would read as a total wipeout. But NOT silent — the reason
    // travels back on the result and is logged by the caller, so "the guard
    // declined because it could not measure" is visible instead of looking
    // identical to "the guard ran and found nothing wrong".
    structuralEdgesError = err instanceof Error ? err.message : String(err);
    logger.warn(
      { err },
      'Structural relationship count failed; the graph-write-collapse check will have no ' +
        'structural measurement from this run.',
    );
  }

  return { nodes: totalNodes, edges: totalEdges, structuralEdges, structuralEdgesError };
};

/**
 * Load cached embeddings from LadybugDB before a rebuild.
 * Returns all embedding vectors so they can be re-inserted after the graph is reloaded,
 * avoiding expensive re-embedding of unchanged nodes.
 *
 * Detects old schema (no chunkIndex column) and returns empty cache to trigger rebuild.
 */
export const loadCachedEmbeddings = async (): Promise<{
  embeddingNodeIds: Set<string>;

View on GitHub (pinned to aac7515d2a)

Solutions

  1. Re-run the analyze — transient timeouts/locks usually clear and the guard gets its measurement next time.
  2. Check the structuralEdgesError string on the load result (it names the exact engine message) before assuming data loss.
  3. Avoid running serve queries against the DB during the load/validation window.
  4. If it persists, rebuild from scratch (`rm -rf .gitnexus && analyze`) to rule out a corrupt rel table.
Defensive patterns

Strategy: validation

Validate before calling

// After load: branch on the structured result, not the log line
const stats = await getGraphStats(dbPath);
if (stats.structuralEdges === undefined) {
  // guard could not measure — do NOT interpret anything about collapse
  console.warn('collapse guard unmeasured:', stats.structuralEdgesError);
}

Type guard

function hasStructuralMeasurement(s: GraphStats): s is GraphStats & { structuralEdges: number } {
  return typeof s.structuralEdges === 'number';
}

Prevention

When it happens

Trigger: The structural count query inside the validation helper throws: connection trouble, query timeout on a huge rel table, missing/corrupt rel table after a bad load. Only the count degrades — nodes/edges totals may still be returned.

Common situations: Very large indexes where COUNT times out; DB contention with a concurrent serve; a load that half-failed earlier (pairs with the lbug-load warn above); LadybugDB extension faults.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20). Data as JSON: /api/errors/feca9642c923096b. Report an issue: GitHub.