moeru-ai/airi · error · ActionError

CRAFTING_FAILED

CRAFTING_FAILED

Error message

Failed to craft ${itemName}

What it means

Wrapped around any exception thrown by mineflayer.bot.craft(recipe, num, table) inside attemptCraft. The underlying mineflayer craft can fail for many reasons: insufficient materials mid-call, recipe mismatch, inventory full, async interruption, or a transient server desync. The library re-throws as CRAFTING_FAILED with the original error in context to preserve the cause while giving a stable code.

Source

Thrown at integrations/minecraft/src/skills/crafting.ts:54

  }

  // Helper function to attempt crafting
  async function attemptCraft(
    recipes: Recipe[] | null,
    craftingTable: Block | null = null,
  ): Promise<boolean> {
    if (recipes && recipes.length > 0) {
      const recipe = recipes[0]
      try {
        await mineflayer.bot.craft(recipe, num, craftingTable ?? undefined)
        logger.log(
          `Successfully crafted ${num} ${itemName}${craftingTable ? ' using crafting table' : ''
          }.`,
        )
        return true
      }
      catch (err) {
        throw new ActionError('CRAFTING_FAILED', `Failed to craft ${itemName}`, { error: err })
      }
    }
    return false
  }

  // Helper function to move to a crafting table and attempt crafting with retry logic
  async function moveToAndCraft(craftingTable: Block): Promise<boolean> {
    logger.log(`Crafting table found, moving to it.`)
    const maxRetries = 2
    let attempts = 0
    let success = false

    while (attempts < maxRetries && !success) {
      try {
        await goToPosition(
          mineflayer,
          craftingTable.position.x,
          craftingTable.position.y,

View on GitHub (pinned to 27111382b4)

Solutions

  1. Free inventory slots before crafting (deposit to chest or drop items).
  2. Re-fetch recipes immediately before craft: const r = mineflayer.bot.recipesFor(id, null, num, table); then craft.
  3. Inspect err.context.error (the wrapped Error) for the true cause — 'Not enough space' vs 'Not enough ingredients' have different fixes.
  4. Retry once after a short delay if the cause looks transient (desync); ensure table is still present.

Example fix

// before
try { await craftRecipe(mineflayer, 'stick', 4) } catch (e) {}

// after
try {
  await craftRecipe(mineflayer, 'stick', 4)
} catch (e) {
  if (e instanceof ActionError && e.code === 'CRAFTING_FAILED') {
    logger.error('underlying cause:', e.context?.error)
    // free inventory, then retry once
    await depositOverflow(mineflayer)
    await craftRecipe(mineflayer, 'stick', 4)
  } else throw e
}
Defensive patterns

Strategy: retry

Validate before calling

async function canCraftNow(mineflayer: Mineflayer, itemId: number, num: number, table: Block | null): Promise<boolean> {
  const recipes = mineflayer.bot.recipesFor(itemId, null, num, table)
  return !!recipes && recipes.length > 0
}

Try / catch

try {
  await craftRecipe(mineflayer, itemName, num)
} catch (e) {
  const cause = (e as ActionError).context?.error as Error | undefined
  if (e instanceof ActionError && e.code === 'CRAFTING_FAILED') {
    // free inventory, re-fetch recipes, retry once
    await depositOverflow(mineflayer)
    await craftRecipe(mineflayer, itemName, num)
  } else throw e
}

Prevention

When it happens

Trigger: bot.craft rejects because the recipe's required count exceeds available items (race between recipesFor check and craft); inventory fills up during crafting (no free slots); the crafting table block became unavailable (broken/moved) between selection and craft; bot was killed or kicked mid-craft; recipe object stale after a server recipe refresh.

Common situations: Inventory nearly full when crafting a stack-producing recipe; concurrent actions modifying inventory; lag spikes causing craft timeout; recipe planner picked a recipe that needs a table but none was passed.

Related errors


AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12). Data as JSON: /api/errors/e53fd8b20454006d. Report an issue: GitHub.