{"record":{"id":"71ebee18cb6bb64f","repo":"linshenkx/prompt-optimizer","slug":"garden-response-is-not-valid-json","errorCode":null,"errorMessage":"Garden response is not valid JSON","messagePattern":"Garden response is not valid JSON","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/ui/src/composables/app/useAppPromptGardenImport.ts","lineNumber":709,"sourceCode":"    }\n\n    return {\n      importCode: snapshot.importCode,\n      optimizerTargetKey,\n      promptFormat: format,\n      promptText,\n      promptMessages,\n      variables: variablesForImport,\n      examples,\n      gardenSnapshot: snapshot,\n    }\n  }\n\n  let data: unknown\n  try {\n    data = JSON.parse(text) as unknown\n  } catch {\n    throw new Error('Garden response is not valid JSON')\n  }\n\n  return parseV1(data)\n}\n\nconst resolveGardenUrl = (opts: { gardenBaseUrl: string | null; url: string }): string | null => {\n  const raw = String(opts.url || '').trim()\n  if (!raw) return null\n  if (/^https?:\\/\\//u.test(raw)) return raw\n\n  const base = opts.gardenBaseUrl ? normalizeBaseUrl(opts.gardenBaseUrl) : null\n  if (!base) return null\n\n  try {\n    return new URL(raw, `${base}/`).toString()\n  } catch {\n    return null\n  }","sourceCodeStart":691,"sourceCodeEnd":727,"githubUrl":"https://github.com/linshenkx/prompt-optimizer/blob/3e677b1d9f7e0493c142c175560531e7ae786dce/packages/ui/src/composables/app/useAppPromptGardenImport.ts#L691-L727","documentation":"The raw response text from the Garden fetch is passed to JSON.parse; if parsing throws (SyntaxError), it is rethrown as 'Garden response is not valid JSON'. This guards parseV1 from receiving non-JSON input such as HTML error pages, plain-text 502 bodies, or truncated responses.","triggerScenarios":"The resolved Garden URL returns HTML (proxy error page, login redirect), a plain-text error, an empty body with 200, or a truncated stream — anything where JSON.parse throws.","commonSituations":"Corporate proxy or CDN intercepting the request with an HTML block page; the URL resolving to the app's own SPA index.html (bad gardenBaseUrl path); auth wall returning an HTML login page with 200; response truncated by network drop or size limits.","solutions":["Log the first ~200 chars of the response text to see what actually came back (usually HTML)","Fix gardenBaseUrl/resolveGardenUrl so the URL points at the JSON API endpoint, not a page route","If a proxy or auth wall is intercepting, fix headers/credentials on the fetch","Check resp.ok and content-type: application/json before parsing, and surface the status alongside this error"],"exampleFix":"// before\ndata = JSON.parse(text)\n\n// after\nconst resp = await fetch(url)\nconst text = await resp.text()\nif (!resp.ok || !(resp.headers.get('content-type') ?? '').includes('json')) {\n  throw new Error(`Garden fetch failed: ${resp.status}`)\n}\nlet data: unknown\ntry { data = JSON.parse(text) } catch {\n  throw new Error(`Garden response is not valid JSON: ${text.slice(0, 120)}`)\n}","handlingStrategy":"try-catch","validationCode":"const text = await resp.text()\nif (!resp.ok) throw new Error(`Garden HTTP ${resp.status}`)\nconst looksJson = /^[\\s\\[\\{]/.test(text)\nif (!looksJson) throw new Error('Non-JSON Garden response')","typeGuard":"const isJsonObjectText = (t: string): boolean => t.trimStart().startsWith('{')","tryCatchPattern":"try { JSON.parse(text) } catch { throw new Error(`Garden response is not valid JSON (first 120 chars): ${text.slice(0, 120)}`) }","preventionTips":["Check content-type includes application/json before parsing","Verify gardenBaseUrl resolves to the API endpoint, not an HTML page","Log response snippets on parse failure to catch proxy/auth interception"],"tags":["json","prompt-garden","fetch","response-parsing"],"backgroundTag":"invalid-json-response","analyzedSha":"3e677b1d9f7e0493c142c175560531e7ae786dce","analyzedAt":"2026-08-27T21:29:16.709Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}