{"record":{"id":"bab854fcfa4b04af","repo":"yikart/AiToEarn","slug":"materialgroupnotfound","errorCode":"MaterialGroupNotFound","errorMessage":"ResponseCode.MaterialGroupNotFound","messagePattern":"ResponseCode\\.MaterialGroupNotFound","errorType":"error_code","errorClass":"AppException","httpStatus":null,"severity":"error","filePath":"project/aitoearn-backend/apps/aitoearn-ai/src/core/ai/video/video.service.ts","lineNumber":66,"sourceCode":"  ) {}\n\n  private requireChannel<T>(service: T | undefined): T {\n    if (!service) {\n      throw new AppException(ResponseCode.InvalidModel)\n    }\n    return service\n  }\n\n  /**\n   * 用户视频生成（通用接口）\n   */\n  async userVideoGeneration(request: UserVideoGenerationRequestDto) {\n    const { model, groupId, userId } = request\n\n    if (groupId) {\n      const group = await this.materialGroupRepository.getInfo(groupId)\n      if (!group || group.userId !== userId) {\n        throw new AppException(ResponseCode.MaterialGroupNotFound)\n      }\n    }\n\n    const modelConfig = this.modelsConfigService.config.video.generation.find(m => m.name === model)\n    if (!modelConfig) {\n      throw new AppException(ResponseCode.InvalidModel)\n    }\n    if (request.mode && !(modelConfig.modes as readonly string[]).includes(request.mode)) {\n      throw new AppException(ResponseCode.InvalidModel)\n    }\n\n    let response: { id: string }\n\n    switch (modelConfig.channel) {\n      case AiLogChannel.Volcengine:\n        response = await this.volcengineVideoService.createFromRequest(request)\n        break\n      case AiLogChannel.OpenAI:","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/apps/aitoearn-ai/src/core/ai/video/video.service.ts#L48-L84","documentation":"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.","triggerScenarios":"request.groupId is set and materialGroupRepository.getInfo(groupId) returns null, or the returned group.userId !== request.userId.","commonSituations":"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.","solutions":["Verify the groupId exists and belongs to the authenticated user before submitting (fetch the user's group list)","Omit groupId if a material group is not required for this generation","Check that the same userId/userType context is used as when the group was created","Re-create the group if it was deleted, then use the new groupId"],"exampleFix":"// before\nawait videoService.userVideoGeneration({ model, groupId: otherUsersGroupId, userId })\n// after\nconst group = await materialGroupRepository.getInfo(groupId)\nif (!group || group.userId !== userId) {\n  throw new Error('Group not found or not owned by user; pick a valid groupId')\n}\nawait videoService.userVideoGeneration({ model, groupId, userId })","handlingStrategy":"validation","validationCode":"const group = await materialGroupRepository.getInfo(groupId)\nif (!group || group.userId !== userId) {\n  throw new Error('Material group not found or not owned by current user')\n}","typeGuard":"function isOwnGroup(g: MaterialGroup | null, userId: string): g is MaterialGroup {\n  return !!g && g.userId === userId\n}","tryCatchPattern":"try {\n  await videoService.userVideoGeneration(req)\n} catch (e) {\n  if (e instanceof AppException && e.code === ResponseCode.MaterialGroupNotFound) {\n    // refresh the user's group list and re-submit with a valid groupId, or drop groupId\n  } else throw e\n}","preventionTips":["Fetch group lists scoped to the current user only","Clear stale groupIds from client state after deletion","Never copy groupIds across users/environments","Validate ownership client-side before submission when the group is already loaded"],"tags":["material-group","not-found","ownership","video"],"backgroundTag":"resource-not-found","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}