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
- 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
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
- 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
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.