moeru-ai/airi · info · ActionError
PLACEMENT_FAILED
PLACEMENT_FAILED
Error message
${blockType} already at ${targetBlock.position} What it means
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 }.
Source
Thrown at integrations/minecraft/src/skills/actions/world-interactions.ts:70
const Item = item.default(mineflayer.bot.version)
await mineflayer.bot.creative.setInventorySlot(36, new Item(itemId, 1)) // 36 is first hotbar slot
}
block = mineflayer.bot.inventory.items().find(item => item.name.includes(blockType))
}
if (!block) {
logger.log(`Don't have any ${blockType} to place.`)
throw new ActionError('ITEM_NOT_FOUND', `Don't have any ${blockType} to place`, { item: blockType })
}
const targetBlock = mineflayer.bot.blockAt(targetDest)
if (!targetBlock) {
logger.log(`No block found at ${targetDest}.`)
throw new ActionError('TARGET_NOT_FOUND', `No block found at ${targetDest}`, { position: targetDest })
}
if (targetBlock.name === blockType) {
logger.log(`${blockType} already at ${targetBlock.position}.`)
throw new ActionError('PLACEMENT_FAILED', `${blockType} already at ${targetBlock.position}`, { blockType, position: targetBlock.position })
}
const emptyBlocks = [
'air',
'water',
'lava',
'grass',
'tall_grass',
'snow',
'dead_bush',
'fern',
]
if (!emptyBlocks.includes(targetBlock.name)) {
logger.log(
`${targetBlock.name} is in the way at ${targetBlock.position}.`,
)
await breakBlockAt(mineflayer, x, y, z)
await sleep(200) // Wait for block to breakView on GitHub (pinned to 27111382b4)
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.
Example fix
// before await placeBlock(bot, 'stone', x, y, z) // throws PLACEMENT_FAILED // after const existing = bot.bot.blockAt(new Vec3(x, y, z)) if (existing?.name !== 'stone') await placeBlock(bot, 'stone', x, y, z)
Defensive patterns
Strategy: validation
Validate before calling
const dest = new Vec3(Math.floor(x), Math.floor(y), Math.floor(z))
const existing = mineflayer.bot.blockAt(dest)
if (!existing || existing.name !== blockType) {
await placeBlock(mineflayer, blockType, x, y, z, placeOn)
} Type guard
const isAlreadyPlaced = (bot: Mineflayer, name: string, pos: Vec3): boolean => bot.bot.blockAt(pos)?.name === name
Try / catch
try {
await placeBlock(mineflayer, blockType, x, y, z, placeOn)
} catch (e) {
if (e instanceof ActionError && e.code === 'PLACEMENT_FAILED' && e.message.includes('already')) {
// treat as success, the block is there
} else throw e
} Prevention
- 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.
When it happens
Trigger: 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'.
Common situations: Idempotent build replays, schematic runner re-executing after a partial failure, AI planner re-issuing the same step, or a misunderstood target block.
Related errors
AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12).
Data as JSON: /api/errors/86cfdc8b5977bc05.
Report an issue: GitHub.