yikart/AiToEarn · error · BadRequestException

image aspectRatio must use WIDTH:HEIGHT

Error message

image aspectRatio must use WIDTH:HEIGHT

What it means

resolveOpenAIImageSize in draft-generation.service.ts converts a user-supplied aspectRatio string into an OpenAI image size like '1024x1024'. It splits on ':', and if the split does not yield exactly 2 parts it throws this BadRequestException. The library enforces the WIDTH:HEIGHT textual format strictly before any numeric validation.

Source

Thrown at project/aitoearn-backend/apps/aitoearn-ai/src/core/draft-generation/draft-generation.service.ts:274

    }

    throw new AppException(ResponseCode.InvalidModel)
  }

  private getImageTextDraftModelConfig(model: string) {
    const modelConfig = config.ai.draftGeneration.imageModels.find(m => m.model === model)
    if (!modelConfig) {
      throw new AppException(ResponseCode.InvalidModel)
    }

    return modelConfig
  }

  private resolveOpenAIImageSize(aspectRatio?: string, imageSize = OPENAI_IMAGE_DEFAULT_RESOLUTION): string {
    const sizeProfile = getOpenAIImageSizeProfile(imageSize)
    const parts = (aspectRatio ?? OPENAI_IMAGE_DEFAULT_ASPECT_RATIO).split(':')
    if (parts.length !== 2) {
      throw new BadRequestException('image aspectRatio must use WIDTH:HEIGHT')
    }

    const widthRatio = Number(parts[0])
    const heightRatio = Number(parts[1])
    if (!Number.isInteger(widthRatio) || !Number.isInteger(heightRatio) || widthRatio <= 0 || heightRatio <= 0) {
      throw new BadRequestException('image aspectRatio must use positive integer WIDTH:HEIGHT')
    }

    const ratio = widthRatio / heightRatio
    if (ratio < OPENAI_IMAGE_MIN_ASPECT_RATIO || ratio > OPENAI_IMAGE_MAX_ASPECT_RATIO) {
      throw new BadRequestException('image aspectRatio must be between 1:3 and 3:1')
    }

    if (widthRatio === heightRatio) {
      return sizeProfile.squareSize ?? this.resolveScaledOpenAIImageSize(widthRatio, heightRatio, sizeProfile)
    }

    return this.resolveScaledOpenAIImageSize(widthRatio, heightRatio, sizeProfile)

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Send aspectRatio in strict 'WIDTH:HEIGHT' form with a colon, e.g. '16:9'.
  2. Normalize the client-side value before the call: replace '-' or 'x' with ':', or omit the field to use the default.
  3. Sanitize whitespace: ' 16:9 ' may still split correctly but '16 : 9' will not.
  4. Check the API docs/OpenAPI schema for the accepted aspectRatio format.

Example fix

// before
body.aspectRatio = '16x9'
// after
body.aspectRatio = '16:9'
Defensive patterns

Strategy: validation

Validate before calling

function isValidAspectRatioFormat(v) { return typeof v === 'string' && /^[1-9]\d*:[1-9]\d*$/.test(v.trim()) }
if (aspectRatio !== undefined && !isValidAspectRatioFormat(aspectRatio)) throw new Error('aspectRatio must be WIDTH:HEIGHT, e.g. 16:9')

Type guard

function isWidthHeightRatio(v: unknown): v is string {
  return typeof v === 'string' && /^[1-9]\d*:[1-9]\d*$/.test(v.trim())
}

Try / catch

try {
  await generateDraft({ aspectRatio })
} catch (e) {
  if (e.status === 400 && /aspectRatio/.test(e.message)) {
    aspectRatio = '1:1' // or normalize 'x'/'-' to ':' and retry
  } else throw e
}

Prevention

When it happens

Trigger: Calling draft generation / OpenAI image resolution with aspectRatio set to values like '16-9', '16x9', '16/9', 'landscape', '1', or an empty-but-present string — anything whose split(':') does not return exactly two tokens.

Common situations: Clients sending UI-provided '16:9' as '16x9'; passing a preset name instead of a ratio; form input not normalized before hitting the API; the default OPENAI_IMAGE_DEFAULT_ASPECT_RATIO only applies when aspectRatio is undefined, not when it is an invalid string.

Related errors


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