musistudio/claude-code-router · error · Error

aspect_ratio must be one of ${constraints.aspectRatios.join(

Error message

aspect_ratio must be one of ${constraints.aspectRatios.join(", ")} for ${target.protocol}.

What it means

Thrown when an aspect_ratio was supplied but is not in the protocol's aspectRatios allowlist. The message lists the accepted values.

Source

Thrown at packages/core/src/media/service.ts:173

    if (!Number.isInteger(durationValue)) throw new Error("duration must be an integer number of seconds.");
    if (constraints.durations && !constraints.durations.includes(durationValue)) {
      throw new Error(`duration must be one of ${constraints.durations.join(", ")} seconds for ${target.protocol}.`);
    }
    if (constraints.durationMinimum !== undefined && constraints.durationMaximum !== undefined &&
      (durationValue < constraints.durationMinimum || durationValue > constraints.durationMaximum)) {
      throw new Error(`duration must be between ${constraints.durationMinimum} and ${constraints.durationMaximum} seconds for ${target.protocol}.`);
    }
    const resolution = optionalString(args.resolution) ?? constraints.defaultResolution;
    if (!resolution) throw new Error(`resolution is required for ${target.protocol}.`);
    if (!constraints.resolutions.includes(resolution)) {
      throw new Error(`resolution must be one of ${constraints.resolutions.join(", ")} for ${target.protocol}.`);
    }
    const aspectRatio = optionalString(args.aspect_ratio);
    if (constraints.requiredParameters.includes("aspect_ratio") && !aspectRatio) {
      throw new Error(`aspect_ratio is required for ${target.protocol}.`);
    }
    if (aspectRatio && !constraints.aspectRatios.includes(aspectRatio)) {
      throw new Error(`aspect_ratio must be one of ${constraints.aspectRatios.join(", ")} for ${target.protocol}.`);
    }
    const request: VideoGenerateRequest = {
      aspectRatio,
      duration: durationValue,
      images: args.images === undefined && args.image === undefined ? [] : this.validateImages(args.images ?? args.image, 1, 7),
      prompt: requiredPrompt(args.prompt),
      resolution
    };
    const job = this.submit("video-generate", request, modelPlan, optionalString(args.idempotency_key));
    return this.publicJob(job);
  }

  getJob(id: string): PublicMediaJob {
    const job = this.jobStore.get(id);
    if (!job) throw new Error(`Media job not found: ${id}`);
    return this.publicJob(job);
  }

View on GitHub (pinned to 99f24806c6)

Solutions

  1. Use an exact value from the error message's allowlist
  2. Build the UI selector from videoGenerationConstraints(protocol).aspectRatios
  3. If aspect ratio is optional, omit it rather than guessing an invalid value

Example fix

// before
media.videoStart({ ..., aspect_ratio: "landscape" }, modelInput);

// after
media.videoStart({ ..., aspect_ratio: "16:9" }, modelInput);
Defensive patterns

Strategy: validation

Validate before calling

const c = videoGenerationConstraints(protocol);
if (args.aspect_ratio && !c.aspectRatios.includes(args.aspect_ratio)) {
  args.aspect_ratio = c.aspectRatios[0]; // snap to first allowed
}

Type guard

const isAllowedAspectRatio = (a: string, c: VideoConstraints): boolean =>
  c.aspectRatios.includes(a);

Prevention

When it happens

Trigger: Passing aspect_ratio: "4:3" to a protocol that only supports ["16:9","9:16"], or using alternate notations like "landscape" or "1.777".

Common situations: Provider-specific ratio vocabularies (16:9 vs widescreen vs 1792x1024); user free-text input; hardcoded values from a different provider integration.

Related errors


AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27). Data as JSON: /api/errors/7dfa5f2ec7a24604. Report an issue: GitHub.