yikart/AiToEarn · error · AppException

MaterialGroupNotFound

MaterialGroupNotFound

Error message

ResponseCode.MaterialGroupNotFound

What it means

MaterialGroupNotFound is thrown by VideoService.userVideoGeneration when a groupId was supplied but the material group either does not exist or is not owned by the requesting user. Group-scoped generation requires the group to belong to the caller.

Source

Thrown at project/aitoearn-backend/apps/aitoearn-ai/src/core/ai/video/video.service.ts:66

  ) {}

  private requireChannel<T>(service: T | undefined): T {
    if (!service) {
      throw new AppException(ResponseCode.InvalidModel)
    }
    return service
  }

  /**
   * 用户视频生成(通用接口)
   */
  async userVideoGeneration(request: UserVideoGenerationRequestDto) {
    const { model, groupId, userId } = request

    if (groupId) {
      const group = await this.materialGroupRepository.getInfo(groupId)
      if (!group || group.userId !== userId) {
        throw new AppException(ResponseCode.MaterialGroupNotFound)
      }
    }

    const modelConfig = this.modelsConfigService.config.video.generation.find(m => m.name === model)
    if (!modelConfig) {
      throw new AppException(ResponseCode.InvalidModel)
    }
    if (request.mode && !(modelConfig.modes as readonly string[]).includes(request.mode)) {
      throw new AppException(ResponseCode.InvalidModel)
    }

    let response: { id: string }

    switch (modelConfig.channel) {
      case AiLogChannel.Volcengine:
        response = await this.volcengineVideoService.createFromRequest(request)
        break
      case AiLogChannel.OpenAI:

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Verify the groupId exists and belongs to the authenticated user before submitting (fetch the user's group list)
  2. Omit groupId if a material group is not required for this generation
  3. Check that the same userId/userType context is used as when the group was created
  4. Re-create the group if it was deleted, then use the new groupId

Example fix

// before
await videoService.userVideoGeneration({ model, groupId: otherUsersGroupId, userId })
// after
const group = await materialGroupRepository.getInfo(groupId)
if (!group || group.userId !== userId) {
  throw new Error('Group not found or not owned by user; pick a valid groupId')
}
await videoService.userVideoGeneration({ model, groupId, userId })
Defensive patterns

Strategy: validation

Validate before calling

const group = await materialGroupRepository.getInfo(groupId)
if (!group || group.userId !== userId) {
  throw new Error('Material group not found or not owned by current user')
}

Type guard

function isOwnGroup(g: MaterialGroup | null, userId: string): g is MaterialGroup {
  return !!g && g.userId === userId
}

Try / catch

try {
  await videoService.userVideoGeneration(req)
} catch (e) {
  if (e instanceof AppException && e.code === ResponseCode.MaterialGroupNotFound) {
    // refresh the user's group list and re-submit with a valid groupId, or drop groupId
  } else throw e
}

Prevention

When it happens

Trigger: request.groupId is set and materialGroupRepository.getInfo(groupId) returns null, or the returned group.userId !== request.userId.

Common situations: Group deleted before generation was submitted; groupId copied from another user's account/workspace; environment mismatch (group created in dev, request in prod); uid spoofing/mismatched auth context so userId differs from the group's owner; id typo or truncation.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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