{"record":{"id":"4fb4f2a300cedc74","repo":"yikart/AiToEarn","slug":"errhttpback-task-no-material","errorCode":"ErrHttpBack.task_no_material","errorMessage":"task_no_material","messagePattern":"task_no_material","errorType":"error_code","errorClass":"AppHttpException","httpStatus":400,"severity":"error","filePath":"project/aitoearn-electron/server/src/modules/task/adminTask.service.ts","lineNumber":108,"sourceCode":"    const updatedTask = await this.taskModel.findByIdAndUpdate(id, data).exec();\n    if (!updatedTask) throw new NotFoundException('Task not found');\n    return updatedTask;\n  }\n\n  /**\n   * 更新任务状态\n   * @param task\n   * @param status\n   * @returns\n   */\n  async updateStatus(task: Task, status: TaskStatus): Promise<Task> {\n    if (status === TaskStatus.ACTIVE && task.type === TaskType.ARTICLE) {\n      // 查询是否存在任务的素材\n      const material = await this.taskMaterialModel.findOne({\n        taskId: task.id,\n      });\n\n      if (!material) throw new AppHttpException(ErrHttpBack.task_no_material);\n    }\n\n    const res = await this.taskModel.findByIdAndUpdate(\n      { _id: task.id },\n      {\n        status,\n      },\n    );\n\n    return res;\n  }\n\n  /**\n   * 更新任务状态\n   * @param id\n   * @param status\n   * @returns\n   */","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/server/src/modules/task/adminTask.service.ts#L90-L126","documentation":"AppHttpException with ErrHttpBack.task_no_material (errCode 90000, message '该任务没有添加素材'). Thrown by AdminTaskService.updateStatus (adminTask.service.ts:108) when an admin activates (status=TaskStatus.ACTIVE) a task of type TaskType.ARTICLE but no TaskMaterial document exists for that taskId. ARTICLE tasks must carry at least one material before going live.","triggerScenarios":"Admin PATCH/POST to update a task's status to ACTIVE where task.type === ARTICLE and taskMaterialModel.findOne({ taskId }) returns null — the article task was created without uploading/attaching material.","commonSituations":"Admin forgets to add material in the admin panel; material was deleted after task creation; task type later switched to ARTICLE without adding material; wrong taskId linkage between task and material collections.","solutions":["Add at least one task material for the task (admin material creation endpoint) before activating it","Verify a material exists: query materials with filter { taskId } and create one if empty","If the task should not require material, change its type away from ARTICLE","Check the material's taskId field actually references this task (not another task's id)"],"exampleFix":"// before\nawait adminTaskApi.updateStatus(taskId, 'ACTIVE') // throws 90000\n// after\nawait adminTaskApi.createTaskMaterial({ taskId, ...material })\nawait adminTaskApi.updateStatus(taskId, 'ACTIVE')","handlingStrategy":"validation","validationCode":"const material = await adminTaskApi.findTaskMaterial({ taskId })\nif (!material && task.type === 'ARTICLE') {\n  throw new Error('Cannot activate ARTICLE task without material')\n}","typeGuard":"function canActivate(task: Task, materials: TaskMaterial[]): boolean {\n  return task.type !== 'ARTICLE' || materials.length > 0\n}","tryCatchPattern":"try {\n  await adminTaskApi.updateStatus(taskId, 'ACTIVE')\n} catch (e) {\n  if (e?.errCode === '90000') {\n    // create a material for the task, then retry activation\n  } else throw e\n}","preventionTips":["Enforce material upload in the admin UI when task type is ARTICLE","Block material deletion while the task is active","Run a pre-activation checklist: type, material presence, taskId linkage"],"tags":["business-rule","validation","admin"],"backgroundTag":"prerequisite-missing","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}