Crosstalk-Solutions/project-nomad · error · Error

Preflight returned no data

Error message

Preflight returned no data

What it means

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'.

Source

Thrown at admin/inertia/components/CountryPickerModal.tsx:150

    if (selected.size === 0) {
      setPreflight(null)
      setErrorMessage(null)
      setLoading(false)
      preflightRequestIdRef.current++
      return
    }

    setErrorMessage(null)
    const timer = setTimeout(async () => {
      const requestId = ++preflightRequestIdRef.current
      setLoading(true)
      try {
        const res = await api.extractMapPreflight({
          countries: [...selected],
          maxzoom,
        })
        if (requestId !== preflightRequestIdRef.current) return
        if (!res) throw new Error('Preflight returned no data')
        setPreflight(res)
      } catch (err: any) {
        if (requestId !== preflightRequestIdRef.current) return
        console.error('Preflight failed:', err)
        setErrorMessage(err?.message ?? 'Estimate failed')
      } finally {
        if (requestId === preflightRequestIdRef.current) setLoading(false)
      }
    }, 1500)

    return () => clearTimeout(timer)
  }, [selected, maxzoom])

  async function startDownload() {
    if (selected.size === 0) {
      setErrorMessage('Pick at least one country before downloading.')
      return
    }

View on GitHub (pinned to 0bd1c6f4f9)

Solutions

  1. Open the browser network tab and inspect the actual response status/body of the extract-map preflight request.
  2. 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.
  3. Check the api.extractMapPreflight wrapper for a silent catch / optional-chain that converts failures to undefined.
  4. Validate selection before calling: require at least one selected country and a sane maxzoom.

Example fix

// before
const res = await api.extractMapPreflight({ countries: [...selected], maxzoom })
if (requestId !== preflightRequestIdRef.current) return
if (!res) throw new Error('Preflight returned no data')
// after
const res = await api.extractMapPreflight({ countries: [...selected], maxzoom })
if (requestId !== preflightRequestIdRef.current) return
if (!res || typeof res.estimated_size !== 'number') {
  throw new Error(res?.message ?? 'Preflight returned no data')
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (selected.size === 0 || maxzoom < 1 || maxzoom > 14) { setErrorMessage('Select at least one country and a valid zoom'); return }

Type guard

type Preflight = { estimated_size: number; [k: string]: unknown }
function isPreflight(v: unknown): v is Preflight {
  return typeof v === 'object' && v !== null && typeof (v as Preflight).estimated_size === 'number'
}

Try / catch

catch (err) {
  if (requestId !== preflightRequestIdRef.current) return // stale, ignore
  setErrorMessage(err?.message ?? 'Estimate failed')
}

Prevention

When it happens

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

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

Related errors


AI-assisted analysis of Crosstalk-Solutions/project-nomad@0bd1c6f4f9 (2026-08-27). Data as JSON: /api/errors/248ac107c44221eb. Report an issue: GitHub.