{"record":{"id":"ab124a91cc4bec5b","repo":"moeru-ai/airi","slug":"invalid-position-to-break-block-at","errorCode":null,"errorMessage":"Invalid position to break block at.","messagePattern":"Invalid position to break block at\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"integrations/minecraft/src/skills/blocks.ts","lineNumber":47,"sourceCode":"  if (isUnbreakableBlock(block))\n    return false\n\n  if (mineflayer.allowCheats) {\n    return breakWithCheats(mineflayer, x, y, z)\n  }\n\n  await moveIntoRange(mineflayer, block)\n\n  if (mineflayer.isCreative) {\n    return breakInCreative(mineflayer, block, x, y, z)\n  }\n\n  return breakInSurvival(mineflayer, block, x, y, z)\n}\n\nfunction validatePosition(x: number, y: number, z: number) {\n  if (x == null || y == null || z == null) {\n    throw new Error('Invalid position to break block at.')\n  }\n}\n\nfunction isUnbreakableBlock(block: any): boolean {\n  return block.name === 'air' || block.name === 'water' || block.name === 'lava'\n}\n\nasync function breakWithCheats(mineflayer: Mineflayer, x: number, y: number, z: number): Promise<boolean> {\n  mineflayer.bot.chat(`/setblock ${Math.floor(x)} ${Math.floor(y)} ${Math.floor(z)} air`)\n  log(mineflayer, `Used /setblock to break block at ${x}, ${y}, ${z}.`)\n  return true\n}\n\nasync function moveIntoRange(mineflayer: Mineflayer, block: any) {\n  if (mineflayer.bot.entity.position.distanceTo(block.position) > 4.5) {\n    const pos = block.position\n    const movements = new Movements(mineflayer.bot)\n    movements.allowParkour = false","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/integrations/minecraft/src/skills/blocks.ts#L29-L65","documentation":"Thrown by validatePosition inside breakBlockAt when any of x, y, or z is null or undefined (uses == null loose equality, catching both). This is a programmer/planner error, not a runtime game-state error: the function signature expects numbers, so a null coordinate means the caller passed garbage. Note this is a plain Error, not an ActionError — it does not carry a code.","triggerScenarios":"Passing undefined to breakBlockAt because the planner destructured a missing field ({x, y} with no z); passing NaN (note: NaN == null is false, so NaN slips through and would fail later at blockAt); JSON config with a null coordinate; optional-chained property access returning undefined.","commonSituations":"LLM planner emits a coordinate object missing a key; position read from a partial block record; off-by-one in an array index returning undefined; refactored call site that previously accepted a Vec3 but now takes three numbers.","solutions":["Validate coordinates at the call site before invoking breakBlockAt: if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z)) throw ...","Coerce with defaults: const { x = 0, y = 0, z = 0 } = pos; — only if zero is a sensible fallback.","Inspect the planner output schema to ensure x, y, z are always present and numeric.","Tighten the function signature to a Vec3 or a branded Position type so TypeScript rejects null at compile time."],"exampleFix":"// before\nbreakBlockAt(mineflayer, pos.x, pos.y, pos.z) // pos.z undefined\n\n// after\nif (pos?.x == null || pos?.y == null || pos?.z == null) {\n  throw new TypeError('breakBlockAt requires finite x, y, z')\n}\nawait breakBlockAt(mineflayer, pos.x, pos.y, pos.z)","handlingStrategy":"validation","validationCode":"function isValidPos(x: unknown, y: unknown, z: unknown): x is number {\n  return typeof x === 'number' && Number.isFinite(x)\n    && typeof y === 'number' && Number.isFinite(y)\n    && typeof z === 'number' && Number.isFinite(z)\n}\nif (!isValidPos(pos.x, pos.y, pos.z)) {\n  throw new TypeError(`Invalid position: ${JSON.stringify(pos)}`)\n}","typeGuard":"type Vec3Like = { x: number; y: number; z: number }\nfunction isVec3(v: unknown): v is Vec3Like {\n  return typeof v === 'object' && v !== null\n    && typeof (v as any).x === 'number'\n    && typeof (v as any).y === 'number'\n    && typeof (v as any).z === 'number'\n}","tryCatchPattern":"if (pos?.x == null || pos?.y == null || pos?.z == null) {\n  throw new TypeError('breakBlockAt requires finite x, y, z')\n}\ntry {\n  await breakBlockAt(mineflayer, pos.x, pos.y, pos.z)\n} catch (e) {\n  if (e instanceof Error && e.message === 'Invalid position to break block at.') {\n    // coordinate source is buggy; log and skip\n  } else throw e\n}","preventionTips":["Type the planner's coordinate output strictly (Vec3, not partial).","Reject NaN explicitly — validatePosition's == null check lets NaN through.","Validate coordinates at the system boundary (LLM output parsing) before passing downstream.","Use a branded Position type so accidental undefined cannot propagate."],"tags":["minecraft","validation","typescript","programming-error"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}