linshenkx/prompt-optimizer · error · Error

Garden response is not valid JSON

Error message

Garden response is not valid JSON

What it means

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.

Source

Thrown at packages/ui/src/composables/app/useAppPromptGardenImport.ts:709

    }

    return {
      importCode: snapshot.importCode,
      optimizerTargetKey,
      promptFormat: format,
      promptText,
      promptMessages,
      variables: variablesForImport,
      examples,
      gardenSnapshot: snapshot,
    }
  }

  let data: unknown
  try {
    data = JSON.parse(text) as unknown
  } catch {
    throw new Error('Garden response is not valid JSON')
  }

  return parseV1(data)
}

const resolveGardenUrl = (opts: { gardenBaseUrl: string | null; url: string }): string | null => {
  const raw = String(opts.url || '').trim()
  if (!raw) return null
  if (/^https?:\/\//u.test(raw)) return raw

  const base = opts.gardenBaseUrl ? normalizeBaseUrl(opts.gardenBaseUrl) : null
  if (!base) return null

  try {
    return new URL(raw, `${base}/`).toString()
  } catch {
    return null
  }

View on GitHub (pinned to 3e677b1d9f)

Solutions

  1. Log the first ~200 chars of the response text to see what actually came back (usually HTML)
  2. Fix gardenBaseUrl/resolveGardenUrl so the URL points at the JSON API endpoint, not a page route
  3. If a proxy or auth wall is intercepting, fix headers/credentials on the fetch
  4. Check resp.ok and content-type: application/json before parsing, and surface the status alongside this error

Example fix

// before
data = JSON.parse(text)

// after
const resp = await fetch(url)
const text = await resp.text()
if (!resp.ok || !(resp.headers.get('content-type') ?? '').includes('json')) {
  throw new Error(`Garden fetch failed: ${resp.status}`)
}
let data: unknown
try { data = JSON.parse(text) } catch {
  throw new Error(`Garden response is not valid JSON: ${text.slice(0, 120)}`)
}
Defensive patterns

Strategy: try-catch

Validate before calling

const text = await resp.text()
if (!resp.ok) throw new Error(`Garden HTTP ${resp.status}`)
const looksJson = /^[\s\[\{]/.test(text)
if (!looksJson) throw new Error('Non-JSON Garden response')

Type guard

const isJsonObjectText = (t: string): boolean => t.trimStart().startsWith('{')

Try / catch

try { JSON.parse(text) } catch { throw new Error(`Garden response is not valid JSON (first 120 chars): ${text.slice(0, 120)}`) }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.

Related errors


AI-assisted analysis of linshenkx/prompt-optimizer@3e677b1d9f (2026-08-27). Data as JSON: /api/errors/71ebee18cb6bb64f. Report an issue: GitHub.