yikart/AiToEarn · error · BadRequestException
image aspectRatio must be between 1:3 and 3:1
Error message
image aspectRatio must be between 1:3 and 3:1
What it means
Once the ratio parses to positive integers, resolveOpenAIImageSize enforces OPENAI_IMAGE_MIN_ASPECT_RATIO..OPENAI_IMAGE_MAX_ASPECT_RATIO (1/3..3). Ratios outside that band cannot map to a supported OpenAI image size and throw this BadRequestException.
Source
Thrown at project/aitoearn-backend/apps/aitoearn-ai/src/core/draft-generation/draft-generation.service.ts:285
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)
}
private resolveScaledOpenAIImageSize(
widthRatio: number,
heightRatio: number,
sizeProfile: { maxEdge: number, maxPixels: number },
): string {
const divisor = getGreatestCommonDivisor(widthRatio, heightRatio)
const reducedWidth = widthRatio / divisor
const reducedHeight = heightRatio / divisor
const maxScaleByEdge = Math.floor(sizeProfile.maxEdge / Math.max(reducedWidth, reducedHeight))View on GitHub (pinned to d3aa8bea5b)
Solutions
- Clamp the ratio to the supported 1:3..3:1 band before sending.
- Pick the closest supported ratio, e.g. '1:3' instead of '1:4'.
- Crop or letterbox the target creative concept to a supported ratio.
- Compute width/height locally and cap the ratio at min(w/h, 3) or floor(max(w/h, 1/3)).
Example fix
// before aspectRatio: '1:4' // ratio 0.25 < 1/3 // after aspectRatio: '1:3' // ratio 0.333, supported
Defensive patterns
Strategy: validation
Validate before calling
function ratioInRange(w, h) { const r = w / h; return r >= 1/3 && r <= 3 }
const [w, h] = aspectRatio.split(':').map(Number)
if (!ratioInRange(w, h)) aspectRatio = clampRatio(w, h) // clamp to 1:3..3:1 Type guard
function isSupportedRatio(v: string): boolean {
const [w, h] = v.split(':').map(Number)
const r = w / h
return r >= 1 / 3 && r <= 3
} Try / catch
try {
await generateDraft({ aspectRatio })
} catch (e) {
if (e.status === 400 && e.message.includes('between 1:3 and 3:1')) {
aspectRatio = clampToSupportedRatio(aspectRatio) // e.g. 1:4 -> 1:3
} else throw e
} Prevention
- Clamp creative ratios to the 1:3..3:1 band before sending.
- Map extreme banner/skyscraper designs to the nearest supported ratio.
- Document the supported range in the UI picker.
- Compute the numeric ratio and compare against 1/3 and 3 client-side.
When it happens
Trigger: aspectRatio like '1:4' (ratio 0.25) or '4:1' (ratio 4) — extreme portrait/landscape ratios beyond 1:3 and 3:1.
Common situations: Generating tall banners (9:32) or wide headers (32:9) for social platforms; passing raw platform creative dimensions without clamping.
Related errors
- imageSize must be one of 1K, 2K, or 4K
- image aspectRatio must use WIDTH:HEIGHT
- image aspectRatio must use positive integer WIDTH:HEIGHT
- video duration is required
- OpenAI does not support multiple images
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/3fc3c64d8911e02b.
Report an issue: GitHub.