yikart/AiToEarn · error · AppException

InvalidModel

InvalidModel

Error message

ResponseCode.InvalidModel

What it means

DraftGenerationPlannerService.planVideo resolves the planner model name (input.plannerModel or the configured default) and looks it up in config.ai.models.chat, requiring the entry to also have 'draft-generation' in its scenes array. If no such chat model config exists, it throws InvalidModel before any AI call is made.

Source

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

  items: z.array(z.object({
    text: z.string().min(1).max(120).describe('Short memory description'),
  })).max(20),
})

export type AutoMemoryResult = z.infer<typeof AutoMemoryResultSchema>

@Injectable()
export class DraftGenerationPlannerService {
  constructor(
    private readonly aiAvailability: AiAvailabilityService,
    @Optional() private readonly relayMediaResolver?: RelayMediaResolverService,
  ) {}

  async planVideo(input: VideoPlanInput): Promise<{ plan: VideoDraftPlanResult, model: string }> {
    const modelName = input.plannerModel ?? config.ai.draftGeneration.planner.defaultModel
    const modelConfig = config.ai.models.chat.find(model => model.name === modelName && model.scenes?.includes('draft-generation'))
    if (!modelConfig) {
      throw new AppException(ResponseCode.InvalidModel)
    }
    const resolvedInput = await this.resolveReferenceUrls(input)
    const prompt = this.buildVideoPrompt(resolvedInput)
    const plan = await this.invokeStructuredPlanner(modelConfig, prompt, VideoDraftPlanResultSchema, resolvedInput.referenceImageUrls)
    return { plan, model: modelConfig.name }
  }

  async planImageText(input: ImageTextPlanInput): Promise<{ plan: ImageTextDraftPlanResult, model: string }> {
    const modelName = input.plannerModel ?? config.ai.draftGeneration.planner.defaultModel
    const modelConfig = config.ai.models.chat.find(model => model.name === modelName && model.scenes?.includes('draft-generation'))
    if (!modelConfig) {
      throw new AppException(ResponseCode.InvalidModel)
    }
    const resolvedInput = await this.resolveReferenceUrls(input)
    const prompt = this.buildImageTextPrompt(resolvedInput)
    const plan = await this.invokeStructuredPlanner(modelConfig, prompt, ImageTextDraftPlanResultSchema, resolvedInput.referenceImageUrls)
    if (plan.imagePrompts.length !== input.imageCount) {
      plan.imagePrompts = Array.from({ length: input.imageCount }, (_, index) => plan.imagePrompts[index] ?? plan.imagePrompts[0] ?? input.userPrompt ?? '')

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Add the requested model to config.ai.models.chat with scenes including 'draft-generation', or pass a plannerModel that matches an existing entry
  2. Log the available chat model names/scenes at startup and compare with the requested modelName
  3. Validate input.plannerModel against the allowed model list in the DTO (Zod enum) before reaching the service
  4. Fix planner.defaultModel in config if the default itself is wrong

Example fix

// before (config)
models:
  chat:
    - name: gpt-4o
      scenes: ['chat']
// after (config)
models:
  chat:
    - name: gpt-4o
      scenes: ['chat', 'draft-generation']
Defensive patterns

Strategy: validation

Validate before calling

const modelName = input.plannerModel ?? config.ai.draftGeneration.planner.defaultModel
const valid = config.ai.models.chat.some(m => m.name === modelName && m.scenes?.includes('draft-generation'))
if (!valid) throw new Error(`Invalid planner model for draft-generation: ${modelName}`)
await plannerService.planVideo(input)

Type guard

function isValidPlannerModel(name: string): boolean {
  return config.ai.models.chat.some(m => m.name === name && m.scenes?.includes('draft-generation'))
}

Try / catch

try {
  const { plan, model } = await plannerService.planVideo(input)
} catch (e) {
  if (e instanceof AppException && e.code === ResponseCode.InvalidModel) {
    // fall back to the configured default planner
    return plannerService.planVideo({ ...input, plannerModel: config.ai.draftGeneration.planner.defaultModel })
  }
  throw e
}

Prevention

When it happens

Trigger: planVideo(input) is called with input.plannerModel set to a name not present in config.ai.models.chat, or present but whose scenes array does not include 'draft-generation'; or the default planner.defaultModel is missing/misnamed in the AI app config.

Common situations: Deployment config (yaml/env-derived config) never registered the chosen model under scenes ['draft-generation']; a model was renamed in the provider but not in app config; caller passes a raw provider model string that has no matching config entry; planner.defaultModel typo after a config refactor.

Related errors


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