codex-team/editor.js · error · Error

Unable to move Block down since it is already the last

Error message

Unable to move Block down since it is already the last

What it means

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.

Source

Thrown at src/components/block-tunes/block-tune-move-down.ts:65

  public render(): TunesMenuConfig {
    return {
      icon: IconChevronDown,
      title: this.api.i18n.t('Move down'),
      onActivate: (): void => this.handleClick(),
      name: 'move-down',
    };
  }

  /**
   * Handle clicks on 'move down' button
   */
  public handleClick(): void {
    const currentBlockIndex = this.api.blocks.getCurrentBlockIndex();
    const nextBlock = this.api.blocks.getBlockByIndex(currentBlockIndex + 1);

    // If Block is last do nothing
    if (!nextBlock) {
      throw new Error('Unable to move Block down since it is already the last');
    }

    const nextBlockElement = nextBlock.holder;
    const nextBlockCoords = nextBlockElement.getBoundingClientRect();

    let scrollOffset = Math.abs(window.innerHeight - nextBlockElement.offsetHeight);

    /**
     * Next block ends on screen.
     * Increment scroll by next block's height to save element onscreen-position
     */
    if (nextBlockCoords.top < window.innerHeight) {
      scrollOffset = window.scrollY + nextBlockElement.offsetHeight;
    }

    window.scrollTo(0, scrollOffset);

    /** Change blocks positions */

View on GitHub (pinned to 5f45dabbe5)

Solutions

  1. Hide or disable the Move Down tune for the last block (check api.blocks.getCurrentBlockIndex() against api.blocks.getBlocksAmount() - 1)
  2. Call api.blocks.move(currentIndex + 1) only when a next block exists
  3. Catch the error at the click-handler level and ignore it for the last block

Example fix

// before
api.blocks.move(api.blocks.getCurrentBlockIndex() + 1);
// after
const idx = api.blocks.getCurrentBlockIndex();
if (idx < api.blocks.getBlocksAmount() - 1) {
  api.blocks.move(idx + 1);
}
Defensive patterns

Strategy: validation

Validate before calling

const idx = api.blocks.getCurrentBlockIndex(); if (idx < api.blocks.getBlocksAmount() - 1) { api.blocks.move(idx + 1); }

Type guard

const canMoveDown = (api: API): boolean => api.blocks.getCurrentBlockIndex() < api.blocks.getBlocksAmount() - 1;

Try / catch

try { tune.handleClick(); } catch (e) { if (e instanceof Error && e.message.includes('already the last')) return; throw e; }

Prevention

When it happens

Trigger: Calling `tune.handleClick()` (or clicking the rendered Move Down tune button) when the focused block is the last block in the editor.

Common situations: 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.

Related errors


AI-assisted analysis of codex-team/editor.js@5f45dabbe5 (2026-08-27). Data as JSON: /api/errors/d4a4e0c4b920caaa. Report an issue: GitHub.