{"record":{"id":"d1b1b26000baef55","repo":"moeru-ai/airi","slug":"resource-missing-d1b1b2","errorCode":"RESOURCE_MISSING","errorMessage":"Cannot craft ${itemName} - missing: ${missingList}","messagePattern":"Cannot craft (.+?) - missing: (.+?)","errorType":"error_code","errorClass":"ActionError","httpStatus":null,"severity":"warning","filePath":"integrations/minecraft/src/skills/crafting.ts","lineNumber":187,"sourceCode":"          const stepRecipes = mineflayer.bot.recipesFor(stepItemId, null, 1, null)\n          if (stepRecipes && stepRecipes.length > 0) {\n            const outputPerCraft = stepRecipes[0].result?.count ?? 1\n            const craftCount = Math.ceil(step.amount / outputPerCraft)\n            await mineflayer.bot.craft(stepRecipes[0], craftCount)\n            logger.log(`Successfully crafted ${craftCount}x ${step.item}`)\n          }\n        }\n      }\n\n      return true\n    }\n\n    // Can't craft - provide helpful error message\n    if (Object.keys(plan.missing).length > 0) {\n      const missingList = Object.entries(plan.missing)\n        .map(([item, count]) => `${count}x ${item}`)\n        .join(', ')\n      throw new ActionError('RESOURCE_MISSING', `Cannot craft ${itemName} - missing: ${missingList}`, {\n        item: itemName,\n        missing: plan.missing,\n      })\n    }\n\n    throw new ActionError('RESOURCE_MISSING', `Cannot craft ${itemName} - missing ingredients`, { item: itemName })\n  }\n\n  // Step 2: Find and use a crafting table\n  // This will throw if it fails hard\n  logger.log(`Step 2: Find and use a crafting table`)\n  const craftingTableRange = 32\n  if (await findAndUseCraftingTable(craftingTableRange)) {\n    return true\n  }\n\n  // If we got here, maybe we didn't have recipes even with a table?\n  // Let's verify if resources are missing","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/integrations/minecraft/src/skills/crafting.ts#L169-L205","documentation":"Thrown by the recursion-guard branch when planRecipe can partially resolve planks/stick/crafting_table but plan.missing has entries — a precise list of ingredients the bot lacks. This is the most actionable crafting failure: the context.missing map tells you exactly what to gather. RESOURCE_MISSING because the path is known but materials are absent.","triggerScenarios":"Trying to craft oak_planks with no logs in inventory (missing: { oak_log: 1 }); crafting a stick without planks; crafting_table without planks; plan can compute the tree but inventory does not satisfy the leaves.","commonSituations":"Bot tried to craft before gathering logs; logs were deposited to a chest; partial inventory after death; LLM skipped the gather step.","solutions":["Read err.context.missing and gather each entry: for 'oak_log' use collectBlock(mineflayer, 'oak_log', count).","Re-run craftRecipe after gathering; the planner will re-evaluate.","If a missing item is itself craftable, recurse via craftRecipe on it first.","Surface the missing list to the planner so it can prioritize harvesting."],"exampleFix":"// before\nawait craftRecipe(mineflayer, 'oak_planks', 4)\n\n// after\ntry {\n  await craftRecipe(mineflayer, 'oak_planks', 4)\n} catch (e) {\n  if (e instanceof ActionError && e.code === 'RESOURCE_MISSING' && e.context?.missing) {\n    for (const [item, count] of Object.entries(e.context.missing)) {\n      await collectBlock(mineflayer, item, count as number)\n    }\n    await craftRecipe(mineflayer, 'oak_planks', 4)\n  } else throw e\n}","handlingStrategy":"validation","validationCode":"function missingMaterials(mineflayer: Mineflayer, itemName: string, num: number): Record<string, number> {\n  const plan = planRecipe(mineflayer.bot, itemName, num)\n  return plan.status === 'ok' ? plan.missing : {}\n}","typeGuard":null,"tryCatchPattern":"try {\n  await craftRecipe(mineflayer, itemName, num)\n} catch (e) {\n  if (e instanceof ActionError && e.code === 'RESOURCE_MISSING' && e.context?.missing) {\n    for (const [item, count] of Object.entries(e.context.missing as Record<string, number>)) {\n      await collectBlock(mineflayer, item, count)\n    }\n    await craftRecipe(mineflayer, itemName, num)\n  } else throw e\n}","preventionTips":["Gather the full ingredient tree before crafting — use planRecipe to enumerate it.","Read context.missing to drive harvesting precisely.","Recurse on missing intermediates that are themselves craftable.","Keep base materials (logs, cobblestone) stocked in a base chest."],"tags":["minecraft","crafting","resources","recipe-planner"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}