{"record":{"id":"86cfdc8b5977bc05","repo":"moeru-ai/airi","slug":"placement-failed","errorCode":"PLACEMENT_FAILED","errorMessage":"${blockType} already at ${targetBlock.position}","messagePattern":"(.+?) already at (.+?)","errorType":"exception","errorClass":"ActionError","httpStatus":null,"severity":"info","filePath":"integrations/minecraft/src/skills/actions/world-interactions.ts","lineNumber":70,"sourceCode":"      const Item = item.default(mineflayer.bot.version)\n      await mineflayer.bot.creative.setInventorySlot(36, new Item(itemId, 1)) // 36 is first hotbar slot\n    }\n    block = mineflayer.bot.inventory.items().find(item => item.name.includes(blockType))\n  }\n  if (!block) {\n    logger.log(`Don't have any ${blockType} to place.`)\n    throw new ActionError('ITEM_NOT_FOUND', `Don't have any ${blockType} to place`, { item: blockType })\n  }\n\n  const targetBlock = mineflayer.bot.blockAt(targetDest)\n  if (!targetBlock) {\n    logger.log(`No block found at ${targetDest}.`)\n    throw new ActionError('TARGET_NOT_FOUND', `No block found at ${targetDest}`, { position: targetDest })\n  }\n\n  if (targetBlock.name === blockType) {\n    logger.log(`${blockType} already at ${targetBlock.position}.`)\n    throw new ActionError('PLACEMENT_FAILED', `${blockType} already at ${targetBlock.position}`, { blockType, position: targetBlock.position })\n  }\n\n  const emptyBlocks = [\n    'air',\n    'water',\n    'lava',\n    'grass',\n    'tall_grass',\n    'snow',\n    'dead_bush',\n    'fern',\n  ]\n  if (!emptyBlocks.includes(targetBlock.name)) {\n    logger.log(\n      `${targetBlock.name} is in the way at ${targetBlock.position}.`,\n    )\n    await breakBlockAt(mineflayer, x, y, z)\n    await sleep(200) // Wait for block to break","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/integrations/minecraft/src/skills/actions/world-interactions.ts#L52-L88","documentation":"Thrown by placeBlock (world-interactions.ts:70) with code PLACEMENT_FAILED when the block at targetDest already has the same name as the requested blockType. This is a no-op guard: the bot has the item and the target is loaded, but the placement would be redundant. Context payload is { blockType, position }.","triggerScenarios":"Call placeBlock('oak_planks', ...) on a position that is already oak_planks, or re-run a build plan without tracking placed positions. The check is exact-name equality, so a 'stone' target will not match 'stone_bricks'.","commonSituations":"Idempotent build replays, schematic runner re-executing after a partial failure, AI planner re-issuing the same step, or a misunderstood target block.","solutions":["Track already-placed positions in your build state and skip them before calling placeBlock.","Treat this error as informational: catch PLACEMENT_FAILED and continue the build plan.","Pre-check with bot.blockAt(targetDest).name === blockType and short-circuit.","If the user expected a different result, verify blockType matches the registry id of what is actually there."],"exampleFix":"// before\nawait placeBlock(bot, 'stone', x, y, z) // throws PLACEMENT_FAILED\n\n// after\nconst existing = bot.bot.blockAt(new Vec3(x, y, z))\nif (existing?.name !== 'stone') await placeBlock(bot, 'stone', x, y, z)","handlingStrategy":"validation","validationCode":"const dest = new Vec3(Math.floor(x), Math.floor(y), Math.floor(z))\nconst existing = mineflayer.bot.blockAt(dest)\nif (!existing || existing.name !== blockType) {\n  await placeBlock(mineflayer, blockType, x, y, z, placeOn)\n}","typeGuard":"const isAlreadyPlaced = (bot: Mineflayer, name: string, pos: Vec3): boolean =>\n  bot.bot.blockAt(pos)?.name === name","tryCatchPattern":"try {\n  await placeBlock(mineflayer, blockType, x, y, z, placeOn)\n} catch (e) {\n  if (e instanceof ActionError && e.code === 'PLACEMENT_FAILED' && e.message.includes('already')) {\n    // treat as success, the block is there\n  } else throw e\n}","preventionTips":["Track placed positions in your build plan and skip them on replay.","Treat PLACEMENT_FAILED with 'already' in the message as a no-op success.","Pre-check blockAt(dest).name before each placement in idempotent loops."],"tags":["minecraft","placement-failed","world-interaction","mineflayer","idempotency"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}