abhigyanpatwari/GitNexus · error · BackendError
server
server
Error message
No response body
What it means
BackendError (code 'server') thrown by parseNdjsonGraphResponse() when response.body is null/undefined on a graph stream request. The NDJSON streaming parser needs a readable body to parse node/relationship records line-by-line; an absent body means the server sent a response with no stream (malformed server behavior or a proxy stripping the body). Status is taken from response.status.
Source
Thrown at gitnexus-web/src/services/backend-client.ts:757
}
const combined = new Uint8Array(downloaded);
let offset = 0;
for (const chunk of chunks) {
combined.set(chunk, offset);
offset += chunk.length;
}
return JSON.parse(new TextDecoder().decode(combined));
};
const parseNdjsonGraphResponse = async (
response: Response,
onProgress?: (downloaded: number, total: number | null) => void,
maxNodes?: number,
maxEdges?: number,
): Promise<{ nodes: GraphNode[]; relationships: GraphRelationship[] }> => {
if (!response.body) {
throw new BackendError('No response body', response.status, 'server');
}
const contentLength = response.headers.get('Content-Length');
const total = contentLength ? parseInt(contentLength, 10) : null;
const reader = response.body.getReader();
const decoder = new TextDecoder();
const nodes: GraphNode[] = [];
const relationships: GraphRelationship[] = [];
let buffer = '';
let downloaded = 0;
// Streaming circuit breaker (#2178): enforce the size limits mid-download as a
// backstop when pre-fetch stats were missing. Same `> threshold` comparison as
// decideSkipGraph. Throwing immediately after the offending push means a later
// error record in the same chunk is never reached — the breaker wins.
const overLimit = (): boolean =>
(typeof maxNodes === 'number' && nodes.length > maxNodes) ||
(typeof maxEdges === 'number' && relationships.length > maxEdges);View on GitHub (pinned to d540b00184)
Solutions
- Check the backend version — the streaming graph endpoint must be implemented and enabled
- Inspect the response headers (Content-Type should be application/x-ndjson) and status in DevTools
- If behind a proxy, disable response buffering for the /api/graph streaming route (e.g. proxy_buffering off in nginx)
- Ensure no other code path consumed response.body before parseNdjsonGraphResponse runs
Example fix
// before — caller assumes the stream is always present
const resp = await fetchGraph(repo);
const { nodes, relationships } = await parseNdjsonGraphResponse(resp); // throws
// after — guard against an absent body
if (!resp.body) throw new Error('Backend returned no graph stream — check backend version/proxy');
const { nodes, relationships } = await parseNdjsonGraphResponse(resp); Defensive patterns
Strategy: validation
Validate before calling
// Check response.body and Content-Type before handing to the NDJSON parser
if (!response.body) {
throw new Error('Backend returned no graph stream — check backend version / proxy buffering');
}
const ct = response.headers.get('Content-Type') ?? '';
if (!ct.includes('ndjson') && !ct.includes('json')) {
throw new Error(`Unexpected graph Content-Type: ${ct}`);
} Type guard
import { BackendError } from './services/backend-client.js';
function isNoBodyError(e: unknown): e is BackendError {
return e instanceof BackendError && e.code === 'server' && /No response body/.test(e.message);
} Try / catch
try {
return await parseNdjsonGraphResponse(response);
} catch (e) {
if (e instanceof BackendError && e.code === 'server' && /No response body/.test(e.message)) {
// backend or proxy stripped the stream — fall back to a non-streaming endpoint or chat-only
enterChatOnlyMode();
return { nodes: [], relationships: [] };
}
throw e;
} Prevention
- Ensure the backend version supports the streaming graph endpoint
- Disable response buffering on reverse proxies for the /api/graph route (nginx: proxy_buffering off)
- Never consume response.body with another reader before parseNdjsonGraphResponse runs
- Check Content-Type is application/x-ndjson before parsing
When it happens
Trigger: A graph fetch (e.g. fetchGraph) returns a Response whose .body is null. This can happen when the server returns a response with Content-Length but no body, when an intermediary proxy strips the streaming body, or when the response was already consumed/locked by another reader.
Common situations: A misconfigured reverse proxy (nginx/cloudflare) buffering or dropping the streaming response body; the backend sent a non-streaming response for a streaming endpoint; the response body was already read by error-handling code before reaching the parser; a server bug returning an empty body for a graph request.
Related errors
- Graph exceeds the size limit (nodes=${nodes.length}, relatio
- ${message}
- sanitized task identity drifted between graph preparation an
- target-controlled {name} must not be a directory
- short write while neutralizing {name}
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/14bb57b8f779384b.
Report an issue: GitHub.