{"record":{"id":"645248d8b5f1c47d","repo":"abhigyanpatwari/GitNexus","slug":"graph-exceeds-the-size-limit-nodes-nodes-length","errorCode":null,"errorMessage":"Graph exceeds the size limit (nodes=${nodes.length}, relationships=${relationships.length})","messagePattern":"Graph exceeds the size limit \\(nodes=(.+?), relationships=(.+?)\\)","errorType":"exception","errorClass":"GraphTooLargeError","httpStatus":null,"severity":"warning","filePath":"gitnexus-web/src/services/backend-client.ts","lineNumber":806,"sourceCode":"      return;\n    }\n    if (record.type === 'relationship') {\n      relationships.push(record.data);\n      return;\n    }\n    if (record.type === 'error') {\n      throw new BackendError(record.error, response.status || 500, 'server');\n    }\n  };\n\n  const tripBreaker = async () => {\n    // Free the socket promptly; never let a cancel rejection mask the breaker.\n    try {\n      await reader.cancel();\n    } catch {\n      // ignore — we're aborting anyway\n    }\n    throw new GraphTooLargeError(\n      `Graph exceeds the size limit (nodes=${nodes.length}, relationships=${relationships.length})`,\n      nodes.length,\n      relationships.length,\n    );\n  };\n\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n\n    downloaded += value.length;\n    onProgress?.(downloaded, total);\n    buffer += decoder.decode(value, { stream: true });\n\n    const lines = buffer.split('\\n');\n    buffer = lines.pop() || '';\n    for (const line of lines) {\n      parseLine(line);","sourceCodeStart":788,"sourceCodeEnd":824,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/gitnexus-web/src/services/backend-client.ts#L788-L824","documentation":"GraphTooLargeError thrown by parseNdjsonGraphResponse() (via tripBreaker) when the streamed node or relationship count crosses the configured size limit (maxNodes/maxEdges) mid-download. This is the streaming circuit breaker (#2178): a backstop for when pre-fetch stats were missing or stale. It cancels the reader to free the socket, then throws with the counts at the point of tripping. connectToServer catches this and falls into chat-only mode rather than letting a huge graph hang the browser.","triggerScenarios":"During NDJSON streaming, after pushing a node or relationship, overLimit() returns true (nodes.length > maxNodes OR relationships.length > maxEdges). tripBreaker() runs: reader.cancel(), then throw GraphTooLargeError with current counts. Defaults come from LARGE_GRAPH_NODE_THRESHOLD / LARGE_GRAPH_EDGE_THRESHOLD in ui-constants.","commonSituations":"Loading a genuinely large repository (monorepo, huge dependency graph) where the pre-fetch stats undercounted or were absent; a repo whose edge count balloons due to dense call graphs; the streaming backstop firing because decideSkipGraph didn't have accurate stats.","solutions":["Let the UI fall into chat-only mode (the intended graceful degradation) — the agent still works without the graph","If you need the graph, reduce the repo scope (analyze a subdirectory or exclude vendor/node_modules)","Raise the thresholds (LARGE_GRAPH_NODE_THRESHOLD / LARGE_GRAPH_EDGE_THRESHOLD) if your browser can handle it — but beware memory pressure","Re-analyze with a tighter include/exclude config to shrink the graph"],"exampleFix":"// before — caller treats graph-too-large as a fatal error\ntry { await loadGraph(repo); }\ncatch (e) { showError('Failed to load graph'); }\n\n// after — degrade to chat-only mode on GraphTooLargeError\ntry { await loadGraph(repo); }\ncatch (e) {\n  if (e instanceof GraphTooLargeError) {\n    enterChatOnlyMode(); // agent still usable, graph view disabled\n  } else throw e;\n}","handlingStrategy":"fallback","validationCode":"// Pre-fetch: use stats to decide whether to attempt the graph load\nimport { decideSkipGraph } from '../lib/graph-load-decision.js';\nconst skip = decideSkipGraph(repo.stats); // returns true if thresholds exceeded\nif (skip) {\n  enterChatOnlyMode(); // skip the streaming fetch entirely\n} else {\n  await loadGraph(repo); // may still trip the streaming breaker\n}","typeGuard":"import { GraphTooLargeError } from './services/backend-client.js';\nfunction isGraphTooLarge(e: unknown): e is GraphTooLargeError {\n  return e instanceof GraphTooLargeError;\n}","tryCatchPattern":"try {\n  return await parseNdjsonGraphResponse(response, onProgress, maxNodes, maxEdges);\n} catch (e) {\n  if (e instanceof GraphTooLargeError) {\n    // e.nodeCount / e.relationshipCount are the counts at trip time\n    enterChatOnlyMode(); // graceful degradation — agent still works\n    return { nodes: [], relationships: [] };\n  }\n  throw e;\n}","preventionTips":["Use pre-fetch stats (decideSkipGraph) to skip obviously large repos before streaming","For huge repos, analyze a subdirectory or exclude vendor/node_modules to shrink the graph","Raise LARGE_GRAPH_NODE_THRESHOLD / LARGE_GRAPH_EDGE_THRESHOLD only if you've tested browser memory headroom","Always handle GraphTooLargeError with a chat-only fallback — it's the designed degradation path, not a crash"],"tags":["graph","streaming","size-limit","performance","graceful-degradation"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}