Crosstalk-Solutions/project-nomad · warning · Error

res.message

Error message

res.message

What it means

runPreflightCheck throws new Error(res.message) when the preflight response object contains a 'message' property. In this backend's convention, a message field on a preflight response signals a refusal or failure explanation (e.g. URL rejected, unreachable host), so the client rethrows it as the user-facing error text. The literal 'res.message' seen in error reports is just interpolation of that backend string.

Source

Thrown at admin/inertia/components/DownloadURLModal.tsx:33

  suggestedURL,
  onPreflightSuccess,
  ...modalProps
}) => {
  const [url, setUrl] = useState<string>('')
  const [messages, setMessages] = useState<string[]>([])
  const [loading, setLoading] = useState<boolean>(false)

  async function runPreflightCheck(downloadUrl: string) {
    try {
      setLoading(true)
      setMessages([`Running preflight check for URL: ${downloadUrl}`])
      const res = await api.downloadRemoteMapRegionPreflight(downloadUrl)
      if (!res) {
        throw new Error('An unknown error occurred during the preflight check.')
      }

      if ('message' in res) {
        throw new Error(res.message)
      }

      setMessages((prev) => [
        ...prev,
        `Preflight check passed. Filename: ${res.filename}, Size: ${(res.size / (1024 * 1024)).toFixed(2)} MB`,
      ])

      if (onPreflightSuccess) {
        onPreflightSuccess(downloadUrl)
      }
    } catch (error) {
      console.error('Preflight check failed:', error)
      setMessages((prev) => [...prev, `Preflight check failed: ${error.message}`])
    } finally {
      setLoading(false)
    }
  }

View on GitHub (pinned to 0bd1c6f4f9)

Solutions

  1. Read the actual res.message content — it is the backend's specific reason and the primary diagnostic.
  2. Verify the URL points directly to a downloadable map region archive on an allowed host.
  3. If the message is unexpected, reproduce the backend's HEAD/GET probe against the URL with curl to see what the host returns.
  4. Improve the backend to use a structured { success, error } shape instead of an ambiguous 'message' key so success payloads can't collide.

Example fix

// before
if ('message' in res) {
  throw new Error(res.message)
}
// after (keep backend text but tag its origin)
if ('message' in res) {
  throw new Error(`Preflight refused: ${res.message}`)
}
Defensive patterns

Strategy: type-guard

Validate before calling

// nothing to run before the call; the guard is on the response
// (validate the URL host against the backend allowlist client-side if known)

Type guard

type PreflightOk = { filename: string; size: number }
type PreflightRefused = { message: string }
function isRefused(v: unknown): v is PreflightRefused {
  return typeof v === 'object' && v !== null && typeof (v as any).message === 'string' && !('filename' in v)
}

Try / catch

if (isRefused(res)) { setMessages((p) => [...p, res.message]); return } // don't throw for expected refusals

Prevention

When it happens

Trigger: The downloadRemoteMapRegionPreflight endpoint returns 200 with { message: '...' } — e.g. the URL points to a disallowed host, the file exceeds size limits, the host is unreachable, or the URL is not a direct map-region archive link.

Common situations: User pastes a URL the backend's allowlist rejects, a link to an HTML page rather than the archive, a host that times out during HEAD probing, or an expired/signed URL whose probe returns an error page.

Related errors


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