{"record":{"id":"7486949abb675984","repo":"abhigyanpatwari/GitNexus","slug":"no-json-found-in-response","errorCode":null,"errorMessage":"No JSON found in response","messagePattern":"No JSON found in response","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"info","filePath":"gitnexus/src/core/ingestion/cluster-enricher.ts","lineNumber":65,"sourceCode":"  return `Analyze this code cluster and provide a semantic name and short description.\n\nHeuristic: \"${heuristicLabel}\"\nMembers: ${memberList}${members.length > 20 ? ` (+${members.length - 20} more)` : ''}\n\nReply with JSON only:\n{\"name\": \"2-4 word semantic name\", \"description\": \"One sentence describing purpose\"}`;\n};\n\n// ============================================================================\n// PARSE LLM RESPONSE\n// ============================================================================\n\nconst parseEnrichmentResponse = (response: string, fallbackLabel: string): ClusterEnrichment => {\n  try {\n    // Extract JSON from response (handles markdown code blocks)\n    const jsonMatch = response.match(/\\{[\\s\\S]*\\}/);\n    if (!jsonMatch) {\n      throw new Error('No JSON found in response');\n    }\n\n    const parsed = JSON.parse(jsonMatch[0]);\n\n    return {\n      name: parsed.name || fallbackLabel,\n      keywords: Array.isArray(parsed.keywords) ? parsed.keywords : [],\n      description: parsed.description || '',\n    };\n  } catch {\n    // Fallback if parsing fails\n    return {\n      name: fallbackLabel,\n      keywords: [],\n      description: '',\n    };\n  }\n};","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/gitnexus/src/core/ingestion/cluster-enricher.ts#L47-L83","documentation":"Thrown inside parseEnrichmentResponse when the LLM's response string contains no substring matching /\\{[\\s\\S]*\\}/ (i.e. no brace-delimited JSON object). IMPORTANT: this throw is fully internal — parseEnrichmentResponse wraps its parsing logic in try/catch and falls back to {name: fallbackLabel, keywords: [], description: ''} on any failure, including this one. A caller of enrichClusters never observes it; it degrades silently to the heuristic label.","triggerScenarios":"The LLM returns plain prose with no JSON object at all (e.g. 'I cannot help with that.'), an empty string, or a response containing only an array (no '{'). Because the regex requires an opening brace, a bare array or scalar text triggers it.","commonSituations":"LLM refused/declined the prompt and returned prose; LLM hit a content filter returning no JSON; truncated response cut off before the JSON; wrong model endpoint returning a status page or error text instead of a completion.","solutions":["This error is already handled — no caller action is required; the cluster simply keeps its heuristic label.","If enrichment quality is poor across many clusters, inspect the raw LLM responses (add logging around llmClient.generate) to see why JSON is missing.","Tune the prompt or switch models if the LLM consistently returns non-JSON.","Verify the LLMClient.generate implementation returns the model's text content, not a wrapper object/stringified HTTP response."],"exampleFix":"// before — mock LLMClient returns prose, triggering the (caught) fallback\nconst client = { generate: async () => 'Sorry, I cannot help.' };\n\n// after — returns the expected JSON shape\nconst client = { generate: async () => '{\"name\":\"Auth Module\",\"description\":\"Handles user authentication\"}' };","handlingStrategy":"fallback","validationCode":"// The error is already caught internally and falls back to the heuristic label.\n// To validate LLM output shape before relying on enrichment fields:\nfunction looksLikeJsonEnvelope(response: string): boolean {\n  return /\\{[\\s\\S]*\\}/.test(response);\n}\nif (!looksLikeJsonEnvelope(await llmClient.generate(prompt))) {\n  logger.warn('LLM returned no JSON envelope — enrichment will fall back');\n}","typeGuard":"function isEnrichmentJson(v: unknown): v is { name?: string; description?: string; keywords?: unknown[] } {\n  return v !== null && typeof v === 'object' && !Array.isArray(v);\n}","tryCatchPattern":"// enrichClusters already handles this internally; callers need no try/catch.\n// If you call parseEnrichmentResponse directly, mirror its catch:\nlet enrichment;\ntry { enrichment = parseEnrichmentResponse(raw, fallbackLabel); }\ncatch { enrichment = { name: fallbackLabel, keywords: [], description: '' }; }","preventionTips":["Trust enrichClusters' built-in fallback — no caller action is required.","If enrichment quality matters, log raw LLM responses and tune the prompt/model.","Confirm llmClient.generate returns model text content, not an HTTP envelope."],"tags":["llm","cluster-enricher","json-parse","fallback-handled","ingestion"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}