abhigyanpatwari/GitNexus · error

[spring-aop] failed to clear synthetic evidence before…

Error message

[spring-aop] failed to clear synthetic evidence before incremental re-write (${message}) — aborting to avoid stale advice metadata; the next run will full-rebuild

What it means

Spring AOP cleanup in lbug-adapter (gitnexus/src/core/lbug/lbug-adapter.ts:2945): before an incremental writeback re-materialises synthetic advice CodeElement nodes, the DETACH DELETE over them failed with something other than benign-missing-table. Continuing would leave stale advice metadata, so the run aborts and the crash-recovery dirty flag forces a full rebuild next run.

Solutions

  1. Re-run the analyze — the next run full-rebuilds from the dirty flag
  2. Stop competing GitNexus processes on the repository before the incremental run
  3. Check the embedded message for disk/lock causes and fix them
  4. Persistent recurrence: gitnexus analyze --force
Defensive patterns

Strategy: retry

Type guard

const isSpringAopClearFailure = (e: unknown): boolean =>
  e instanceof Error &&
  e.message.startsWith('[spring-aop] failed to clear synthetic evidence');

Try / catch

try {
  await clearSpringAopEvidence();
} catch (e) {
  if (isSpringAopClearFailure(e)) {
    // stale advice metadata risk — abort writeback, let the dirty flag force a full rebuild
    logger.warn('spring-aop cleanup failed; next run will full-rebuild');
  }
  throw e;
}

Prevention

When it happens

Trigger: Incremental writeback on a repo with Spring AOP evidence where the synthetic-node DELETE fails — lock contention with a concurrent reader, disk error, or native engine failure.

Common situations: Concurrent serve/MCP traffic on the same index during analyze; AV or disk pressure during the delete phase; an index recovering from an earlier crashed run.

Related errors


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

Appendix: source

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

        `MATCH (n:CodeElement) WHERE ${predicate} RETURN count(n) AS cnt`,
      );
      const result = Array.isArray(countResult) ? countResult[0] : countResult;
      const rows = await result.getAll();
      const count = Number(rows[0]?.cnt ?? rows[0]?.[0] ?? 0);
      if (count > 0) {
        await closeQueryResults(
          await c.query(`MATCH (n:CodeElement) WHERE ${predicate} DETACH DELETE n`),
        );
      }
      if (countResult) await closeQueryResults(countResult);
      return { nodesDeleted: count };
    } catch (err) {
      if (countResult) await closeQueryResults(countResult);
      if (classifyDeleteAllError(err) === 'benign-missing-table') {
        return { nodesDeleted: 0 };
      }
      const message = err instanceof Error ? err.message : String(err);
      throw new Error(
        '[spring-aop] failed to clear synthetic evidence before incremental re-write ' +
          `(${message}) — aborting to avoid stale advice metadata; the next run will full-rebuild`,
      );
    }
  });
};

/**
 * Drop EVERY `Destination` node before an incremental writeback, so the async
 * messaging overlay is rebuilt whole from the fresh graph.
 *
 * Delete-all rather than delete-by-file, because the file-keyed rule cannot
 * express this layer in either direction. A RESOLVED destination stores no
 * `filePath` — that is what stops `deleteNodesForFiles` cutting a node shared
 * by files outside the write set — which also means it is never deleted when it
 * SHOULD be, so a destination whose last referrer stopped naming it survived as
 * an edgeless orphan that still carried `address`, the cross-repository join
 * key, accumulating on every run. The mirror defect was worse: without a

View on GitHub (pinned to ac9a4e9abd)