{"record":{"id":"d3629b4378e463e2","repo":"Crosstalk-Solutions/project-nomad","slug":"failed-to-dispatch-download-job","errorCode":null,"errorMessage":"Failed to dispatch download job","messagePattern":"Failed to dispatch download job","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"admin/app/services/map_service.ts","lineNumber":303,"sourceCode":"    // Parse resource metadata\n    const parsedFilename = CollectionManifestService.parseMapFilename(filename)\n    const resourceMetadata = parsedFilename\n      ? { resource_id: parsedFilename.resource_id, version: parsedFilename.version, collection_ref: null }\n      : undefined\n\n    // Dispatch background job\n    const result = await RunDownloadJob.dispatch({\n      url,\n      filepath,\n      timeout: 30000,\n      allowedMimeTypes: PMTILES_MIME_TYPES,\n      forceNew: true,\n      filetype: 'map',\n      resourceMetadata,\n    })\n\n    if (!result.job) {\n      throw new Error('Failed to dispatch download job')\n    }\n\n    logger.info(`[MapService] Dispatched download job ${result.job.id} for URL ${url}`)\n\n    return {\n      filename,\n      jobId: result.job?.id,\n    }\n  }\n\n  async downloadRemotePreflight(\n    url: string\n  ): Promise<{ filename: string; size: number } | { message: string }> {\n    try {\n      assertNotPrivateUrl(url)\n      const parsed = new URL(url)\n      if (!parsed.pathname.endsWith('.pmtiles')) {\n        throw new Error(`Invalid PMTiles file URL: ${url}. URL must end with .pmtiles`)","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/Crosstalk-Solutions/project-nomad/blob/0bd1c6f4f9888d577fe232de06ac144bb8337131/admin/app/services/map_service.ts#L285-L321","documentation":"downloadRemote delegates the actual fetch to a download job system (dispatchDownloadJob-like call with forceNew and resourceMetadata). If that dispatcher returns a result without a job object, the service cannot proceed and throws this error, meaning the job queue accepted the request but produced no job record.","triggerScenarios":"Calling downloadRemote when the job dispatch layer fails internally — e.g. database/queue unavailable, job creation rejected, or the dispatcher returns { job: null } due to an unexpected filetype/metadata combination.","commonSituations":"Job queue (DB/Redis) not running or misconfigured; migrations for the run-download-jobs table not applied; version mismatch between map_service and the job dispatcher API; concurrent dispatch hitting a unique constraint that swallows the job.","solutions":["Inspect server logs immediately before this line for the dispatcher's underlying error","Verify the job queue backend (DB/Redis) is up and the run_download_jobs table exists (run migrations)","Reproduce by calling downloadRemote after confirming dispatch works for other filetypes","Retry the call once transient queue issues are resolved"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { const res = await mapService.downloadRemote(url) } catch (e) { if (e instanceof Error && e.message === 'Failed to dispatch download job') { /* inspect job queue backend health, then surface to user */ } throw e }","preventionTips":["Keep the job queue backend (DB/Redis) in health checks","Apply job-table migrations before deploying map download features","Add idempotency so re-dispatching after a partial failure is safe"],"tags":["job-queue","download","dispatch"],"backgroundTag":"job-dispatch-failed","analyzedSha":"0bd1c6f4f9888d577fe232de06ac144bb8337131","analyzedAt":"2026-08-27T05:34:15.424Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}