{"record":{"id":"d4a4e0c4b920caaa","repo":"codex-team/editor.js","slug":"unable-to-move-block-down-since-it-is-already-the","errorCode":null,"errorMessage":"Unable to move Block down since it is already the last","messagePattern":"Unable to move Block down since it is already the last","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/components/block-tunes/block-tune-move-down.ts","lineNumber":65,"sourceCode":"  public render(): TunesMenuConfig {\n    return {\n      icon: IconChevronDown,\n      title: this.api.i18n.t('Move down'),\n      onActivate: (): void => this.handleClick(),\n      name: 'move-down',\n    };\n  }\n\n  /**\n   * Handle clicks on 'move down' button\n   */\n  public handleClick(): void {\n    const currentBlockIndex = this.api.blocks.getCurrentBlockIndex();\n    const nextBlock = this.api.blocks.getBlockByIndex(currentBlockIndex + 1);\n\n    // If Block is last do nothing\n    if (!nextBlock) {\n      throw new Error('Unable to move Block down since it is already the last');\n    }\n\n    const nextBlockElement = nextBlock.holder;\n    const nextBlockCoords = nextBlockElement.getBoundingClientRect();\n\n    let scrollOffset = Math.abs(window.innerHeight - nextBlockElement.offsetHeight);\n\n    /**\n     * Next block ends on screen.\n     * Increment scroll by next block's height to save element onscreen-position\n     */\n    if (nextBlockCoords.top < window.innerHeight) {\n      scrollOffset = window.scrollY + nextBlockElement.offsetHeight;\n    }\n\n    window.scrollTo(0, scrollOffset);\n\n    /** Change blocks positions */","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/codex-team/editor.js/blob/5f45dabbe5690b7033b2f9047d3985add5045fdd/src/components/block-tunes/block-tune-move-down.ts#L47-L83","documentation":"The 'Move down' block tune fires when the user clicks its button; it refuses to operate when the current block is the last one because there is no block below to swap with. The check uses getBlockByIndex(currentIndex + 1) and throws when it returns null.","triggerScenarios":"Calling `tune.handleClick()` (or clicking the rendered Move Down tune button) when the focused block is the last block in the editor.","commonSituations":"The tune is rendered for the last block and the user clicks it; programmatic invocation of the move-down handler on the final block; UI that shows the button enabled for the last block.","solutions":["Hide or disable the Move Down tune for the last block (check api.blocks.getCurrentBlockIndex() against api.blocks.getBlocksAmount() - 1)","Call api.blocks.move(currentIndex + 1) only when a next block exists","Catch the error at the click-handler level and ignore it for the last block"],"exampleFix":"// before\napi.blocks.move(api.blocks.getCurrentBlockIndex() + 1);\n// after\nconst idx = api.blocks.getCurrentBlockIndex();\nif (idx < api.blocks.getBlocksAmount() - 1) {\n  api.blocks.move(idx + 1);\n}","handlingStrategy":"validation","validationCode":"const idx = api.blocks.getCurrentBlockIndex(); if (idx < api.blocks.getBlocksAmount() - 1) { api.blocks.move(idx + 1); }","typeGuard":"const canMoveDown = (api: API): boolean => api.blocks.getCurrentBlockIndex() < api.blocks.getBlocksAmount() - 1;","tryCatchPattern":"try { tune.handleClick(); } catch (e) { if (e instanceof Error && e.message.includes('already the last')) return; throw e; }","preventionTips":["Check index against getBlocksAmount() before moving","Disable the tune button for the last block"],"tags":["block-tune","move-down","bounds-check","editorjs"],"backgroundTag":"index-out-of-bounds","analyzedSha":"5f45dabbe5690b7033b2f9047d3985add5045fdd","analyzedAt":"2026-08-27T22:50:23.124Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}