yikart/AiToEarn · warning

InvalidUrl

InvalidUrl

Error message

Failed to probe image ${m.url}

What it means

During validateMedia, a non-video media entry is fetched with probeImage to check dimensions/format against platform rules. If probeImage throws (unreachable URL, non-image content, timeout, oversized body), the error is logged as 'Failed to probe image <url>' and a PublishValidationIssue with code InvalidUrl is appended instead of failing the request.

Source

Thrown at project/aitoearn-backend/apps/aitoearn-server/src/core/channels/media/media.service.ts:396

            allowUnknownFormat: mediaType === PublishMediaType.Video && !urlExtension,
          }))
        }
        catch (err) {
          this.logger.warn(err, `Failed to probe video ${m.url}`)
          issues.push({
            code: PublishValidationIssueCode.InvalidUrl,
            path: pathPrefix,
            params: { field: PublishValidationField.Video, url: m.url },
          })
        }
      }
      else {
        try {
          const probe = await this.probeImage(m.url)
          issues.push(...this.validateImage(probe, rules, pathPrefix))
        }
        catch (err) {
          this.logger.warn(err, `Failed to probe image ${m.url}`)
          issues.push({
            code: PublishValidationIssueCode.InvalidUrl,
            path: pathPrefix,
            params: { field: PublishValidationField.Image, url: m.url },
          })
        }
      }
    }
    if (content.cover?.url) {
      const pathPrefix = ['content', 'cover']
      try {
        const probe = await this.probeImage(content.cover.url)
        issues.push(...this.validateImage(probe, rules, pathPrefix))
      }
      catch (err) {
        this.logger.warn(err, `Failed to probe cover ${content.cover.url}`)
        issues.push({
          code: PublishValidationIssueCode.InvalidUrl,

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Fix or re-upload the media at the failing URL reported in the issue params.url, then re-submit validation.
  2. Verify the URL is publicly reachable (curl -I) and serves an image content type with no auth required.
  3. If the file is actually a video, ensure the media type/extension classifies it as video so the video probe path runs.
  4. Extend media URL TTL or move files to stable storage before calling validateMedia.

Example fix

// before
url: 'https://cdn.example.com/tmp/abc?X-Amz-Expires=60'
// after: issue a long-lived public URL before validation
url: await storage.getPublicUrl(key, { expiresIn: '24h' })
Defensive patterns

Strategy: validation

Validate before calling

// Before calling validateMedia, ensure each media URL is fetchable
async function assertImageFetchable(url: string) {
  const res = await fetch(url, { method: 'HEAD' })
  if (!res.ok) throw new Error(`URL not fetchable: ${url} (${res.status})`)
  const ct = res.headers.get('content-type') ?? ''
  if (!ct.startsWith('image/')) throw new Error(`Not an image: ${url} (${ct})`)
}

Type guard

function hasValidUrl(m: { url?: string }): m is { url: string } {
  return typeof m.url === 'string' && /^https:\/\//.test(m.url)
}

Try / catch

const issues = await mediaService.validateMedia(content, rules)
if (issues.some(i => i.code === PublishValidationIssueCode.InvalidUrl)) {
  // surface bad URLs (issue.params.url) back to the user for re-upload
}

Prevention

When it happens

Trigger: content.media[i].url points to a dead/hostile URL: 404/410 from expired CDN link, DNS failure, non-2xx response, response Content-Type not an image, body exceeds probe size limits, or TLS error.

Common situations: Presigned upload URLs expired before publishing; client submitted a placeholder or localhost URL; storage bucket object deleted; image host blocks the server's user-agent/region; wrong file extension causing video/image branch confusion is irrelevant here (this branch is the non-video fallback).

Related errors


AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31). Data as JSON: /api/errors/a694e78cd9685acf. Report an issue: GitHub.