{"record":{"id":"ed7d0b1ee4b46e66","repo":"yikart/AiToEarn","slug":"error-ed7d0b","errorCode":null,"errorMessage":"任务素材不存在","messagePattern":"任务素材不存在","errorType":"http","errorClass":"NotFoundException","httpStatus":404,"severity":"error","filePath":"project/aitoearn-electron/server/src/modules/task/task.controller.ts","lineNumber":88,"sourceCode":"    return await this.taskService.findOne(id);\n  }\n\n  @ApiOperation({ summary: '申请任务: 保持一定时间' })\n  @ApiResult({ type: Boolean })\n  @Post('apply/:id')\n  async applyForTask(\n    @Param('id') id: string,\n    @GetToken() token: TokenInfo,\n    @Body() body: ApplyTaskDto,\n  ) {\n    const task = await this.taskService.findOne(id);\n    if (!task) throw new NotFoundException('任务不存在');\n\n    if (!!body.taskMaterialId) {\n      const taskMaterial = await this.taskService.getTaskMaterialById(\n        body.taskMaterialId,\n      );\n      if (!taskMaterial) throw new NotFoundException('任务素材不存在');\n    }\n\n    const res = await this.userTaskService.userApplyTask(token.id, task, body);\n\n    if (!!body.taskMaterialId)\n      this.taskService.upTaskMaterialUsedCount(body.taskMaterialId);\n\n    return res;\n  }\n\n  @ApiOperation({ summary: '提交任务 id是用户任务ID' })\n  @ApiResult({ type: Boolean })\n  @Post('submit/:id')\n  async submitTask(\n    @GetToken() token: TokenInfo,\n    @Param('id') id: string,\n    @Body() data: SubmitTaskDto,\n  ) {","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/server/src/modules/task/task.controller.ts#L70-L106","documentation":"HTTP 404 NotFoundException thrown by POST task-apply when body.taskMaterialId is provided but no TaskMaterial document with that id exists. The controller checks the task first, then resolves the referenced material before creating the user-task application. It exists to guarantee every application references a real material record.","triggerScenarios":"POST to the task apply endpoint with a body containing taskMaterialId that does not match any taskMaterial document: a stale/hardcoded id, an id from a deleted material, an id belonging to another task, or a malformed ObjectId.","commonSituations":"Client caches material ids after an admin deletes/recreates materials; tester copies a request sample with a made-up taskMaterialId; frontend passes the task id instead of the material id.","solutions":["Fetch the material list for the task and use a taskMaterialId from the current response","Verify the material id actually belongs to the task being applied for (it must be findable via getTaskMaterialById)","If the caller does not need a material, omit taskMaterialId entirely — the check only runs when it is truthy","Confirm MongoDB connectivity if a known-good id suddenly 404s"],"exampleFix":"// before\nawait api.applyForTask(taskId, { taskMaterialId: '64a1b2c3' }) // stale id\n// after\nconst task = await api.getTask(taskId)\nawait api.applyForTask(taskId, { taskMaterialId: task.materials[0].id })","handlingStrategy":"validation","validationCode":"const materials = await api.getTaskMaterials(taskId)\nif (!materials.some(m => m.id === body.taskMaterialId)) {\n  throw new Error('Invalid taskMaterialId for this task')\n}","typeGuard":"function hasValidMaterialId(body: unknown): body is { taskMaterialId: string } {\n  return typeof (body as any)?.taskMaterialId === 'string' && /^[a-f\\d]{24}$/i.test((body as any).taskMaterialId)\n}","tryCatchPattern":"try {\n  await api.applyForTask(taskId, body)\n} catch (e) {\n  if (e?.status === 404 && e?.message === '任务素材不存在') {\n    // refresh material list and re-pick a valid taskMaterialId\n  } else throw e\n}","preventionTips":["Always source taskMaterialId from a fresh material-list response, never cache it","Omit taskMaterialId when the task has no material requirement","Validate the id is a 24-char hex ObjectId before sending"],"tags":["http-404","validation","mongodb"],"backgroundTag":"resource-not-found","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}