{"record":{"id":"248ac107c44221eb","repo":"Crosstalk-Solutions/project-nomad","slug":"preflight-returned-no-data","errorCode":null,"errorMessage":"Preflight returned no data","messagePattern":"Preflight returned no data","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"admin/inertia/components/CountryPickerModal.tsx","lineNumber":150,"sourceCode":"    if (selected.size === 0) {\n      setPreflight(null)\n      setErrorMessage(null)\n      setLoading(false)\n      preflightRequestIdRef.current++\n      return\n    }\n\n    setErrorMessage(null)\n    const timer = setTimeout(async () => {\n      const requestId = ++preflightRequestIdRef.current\n      setLoading(true)\n      try {\n        const res = await api.extractMapPreflight({\n          countries: [...selected],\n          maxzoom,\n        })\n        if (requestId !== preflightRequestIdRef.current) return\n        if (!res) throw new Error('Preflight returned no data')\n        setPreflight(res)\n      } catch (err: any) {\n        if (requestId !== preflightRequestIdRef.current) return\n        console.error('Preflight failed:', err)\n        setErrorMessage(err?.message ?? 'Estimate failed')\n      } finally {\n        if (requestId === preflightRequestIdRef.current) setLoading(false)\n      }\n    }, 1500)\n\n    return () => clearTimeout(timer)\n  }, [selected, maxzoom])\n\n  async function startDownload() {\n    if (selected.size === 0) {\n      setErrorMessage('Pick at least one country before downloading.')\n      return\n    }","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/Crosstalk-Solutions/project-nomad/blob/0bd1c6f4f9888d577fe232de06ac144bb8337131/admin/inertia/components/CountryPickerModal.tsx#L132-L168","documentation":"In CountryPickerModal, after calling api.extractMapPreflight for the selected countries and maxzoom, a null/undefined response triggers this throw. The guard exists because the subsequent code assumes preflight data (size estimates) exists; the error is surfaced via setErrorMessage as 'Preflight failed: <message>' or 'Estimate failed'.","triggerScenarios":"api.extractMapPreflight resolves to null/undefined — e.g. the API wrapper swallows an error body and returns undefined, the request was aborted, or the backend returned an empty 200 response for the given countries/maxzoom combination.","commonSituations":"Backend endpoint changed its response shape (no longer returns the estimate object); API client version mismatch after upgrade; backend returns 204 No Content when no tiles match the selection; a middleware returning null on validation failure without an error status.","solutions":["Open the browser network tab and inspect the actual response status/body of the extract-map preflight request.","If the backend returns 204 or empty body for empty selections, guard on the server side to always return an estimate object or a 4xx with a message.","Check the api.extractMapPreflight wrapper for a silent catch / optional-chain that converts failures to undefined.","Validate selection before calling: require at least one selected country and a sane maxzoom."],"exampleFix":"// before\nconst res = await api.extractMapPreflight({ countries: [...selected], maxzoom })\nif (requestId !== preflightRequestIdRef.current) return\nif (!res) throw new Error('Preflight returned no data')\n// after\nconst res = await api.extractMapPreflight({ countries: [...selected], maxzoom })\nif (requestId !== preflightRequestIdRef.current) return\nif (!res || typeof res.estimated_size !== 'number') {\n  throw new Error(res?.message ?? 'Preflight returned no data')\n}","handlingStrategy":"try-catch","validationCode":"if (selected.size === 0 || maxzoom < 1 || maxzoom > 14) { setErrorMessage('Select at least one country and a valid zoom'); return }","typeGuard":"type Preflight = { estimated_size: number; [k: string]: unknown }\nfunction isPreflight(v: unknown): v is Preflight {\n  return typeof v === 'object' && v !== null && typeof (v as Preflight).estimated_size === 'number'\n}","tryCatchPattern":"catch (err) {\n  if (requestId !== preflightRequestIdRef.current) return // stale, ignore\n  setErrorMessage(err?.message ?? 'Estimate failed')\n}","preventionTips":["Use the requestId staleness guard before both the throw and the catch (already partially present)","Ensure the API wrapper throws on non-OK rather than returning undefined","Validate selection and maxzoom before invoking preflight"],"tags":["react","inertia","preflight","api-client","maps"],"backgroundTag":"empty-api-response","analyzedSha":"0bd1c6f4f9888d577fe232de06ac144bb8337131","analyzedAt":"2026-08-27T05:34:15.424Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}