Crosstalk-Solutions/project-nomad · error · Error

Could not determine filename from URL

Error message

Could not determine filename from URL

What it means

Thrown by MapService.downloadRemote when the last path segment of the provided URL cannot be used as a filename. Because url.split('/').pop() only returns empty for URLs ending in '/' (or being just '/'), this fires when the URL has no filename component and the service has nothing to name the downloaded PMTiles file.

Source

Thrown at admin/app/services/map_service.ts:271

        logger.error(`[MapService] Failed to create InstalledResource for ${filename}:`, error)
      }
    }
  }

  async downloadRemote(url: string): Promise<{ filename: string; jobId?: string }> {
    const parsed = new URL(url)
    if (!parsed.pathname.endsWith('.pmtiles')) {
      throw new Error(`Invalid PMTiles file URL: ${url}. URL must end with .pmtiles`)
    }

    const existing = await RunDownloadJob.getActiveByUrl(url)
    if (existing) {
      throw new Error(`Download already in progress for URL ${url}`)
    }

    const filename = url.split('/').pop()
    if (!filename) {
      throw new Error('Could not determine filename from URL')
    }

    const filepath = join(process.cwd(), this.mapStoragePath, 'pmtiles', filename)


    // First, ensure base assets are present - regions depend on them
    const baseAssetsExist = await this.ensureBaseAssets()
    if (!baseAssetsExist) {
      throw new Error(
        'Base map assets are missing and could not be downloaded. Please check your connection and try again.'
      )
    }

    // Parse resource metadata
    const parsedFilename = CollectionManifestService.parseMapFilename(filename)
    const resourceMetadata = parsedFilename
      ? { resource_id: parsedFilename.resource_id, version: parsedFilename.version, collection_ref: null }
      : undefined

View on GitHub (pinned to 0bd1c6f4f9)

Solutions

  1. Ensure the URL points to an actual .pmtiles file, not a directory (remove trailing slash or append the filename)
  2. Validate the URL's last segment before calling downloadRemote (e.g. new URL(url).pathname.split('/').pop() is non-empty and ends with .pmtiles)
  3. If the URL comes from UI input, add client-side validation with a clear message

Example fix

// before
await mapService.downloadRemote('https://tiles.example.com/pmtiles/')

// after
await mapService.downloadRemote('https://tiles.example.com/pmtiles/region.pmtiles')
Defensive patterns

Strategy: validation

Validate before calling

function getFilenameFromUrl(url: string): string | null {
  try {
    const seg = new URL(url).pathname.split('/').filter(Boolean).pop()
    return seg && seg.endsWith('.pmtiles') ? seg : null
  } catch { return null }
}
const filename = getFilenameFromUrl(url)
if (!filename) throw new Error('URL must point to a .pmtiles file')
await mapService.downloadRemote(url)

Type guard

const isFileUrl = (u: string): boolean => {
  try { return new URL(u).pathname.split('/').pop() !== '' } catch { return false }
}

Try / catch

try { await mapService.downloadRemote(url) } catch (e) { if (e instanceof Error && e.message.includes('Could not determine filename')) { /* fix URL, re-prompt user */ } else throw e }

Prevention

When it happens

Trigger: Calling downloadRemote with a URL like 'https://example.com/pmtiles/' (trailing slash, no filename) or 'https://example.com/' — any URL whose last segment after the final '/' is an empty string.

Common situations: Building the URL from user input or a directory listing without trimming/validating; concatenating a base URL with a missing filename variable; copy-pasting a directory URL instead of a file URL.

Related errors


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