kovidgoyal/kitty · info

Pending mode change to already current mode (%d) requested.

Error message

Pending mode change to already current mode (%d) requested. Either pending mode expired or there is an application bug.

What it means

An application toggled DEC private mode 2026 (pending/synchronized update mode) to the value already current - screen_pause_rendering returned false. Either the ~250ms pending window expired and the app re-issued the same change, or the app has a state bug. Rendering continues unsynchronized.

Source

Thrown at kitty/screen.c:2035

            if (val) {
                // When DECCOLM mode is set, the screen is erased and the cursor
                // moves to the home position.
                screen_erase_in_display(self, 2, false);
                screen_cursor_position(self, 1, 1);
            }
            break;
        case CONTROL_CURSOR_BLINK: self->cursor->non_blinking = !val; break;
        case SAVE_CURSOR: screen_save_cursor(self); break;
        case TOGGLE_ALT_SCREEN_1:
        case TOGGLE_ALT_SCREEN_2:
        case ALTERNATE_SCREEN:
            if (val && self->linebuf == self->main_linebuf) screen_toggle_screen_buffer(self, mode == ALTERNATE_SCREEN, mode == ALTERNATE_SCREEN);
            else if (!val && self->linebuf != self->main_linebuf) screen_toggle_screen_buffer(self, mode == ALTERNATE_SCREEN, mode == ALTERNATE_SCREEN);
            break;
        case 7727 << 5: log_error("Application escape mode is not supported, the extended keyboard protocol should be used instead"); break;
        case PENDING_MODE << 5:
            if (!screen_pause_rendering(self, val, 0)) {
                log_error("Pending mode change to already current mode (%d) requested. Either pending mode expired or there is an application bug.", val);
            }
            break;
        case INBAND_RESIZE_NOTIFICATION:
            self->modes.mINBAND_RESIZE_NOTIFICATION = val;
            if (val) CALLBACK("notify_child_of_resize", NULL);
            break;
        case VISIBILITY_REPORTS:
            self->modes.mVISIBILITY_REPORTS = val;
            if (val) send_visibility_report(self);
            break;
        default:
            private = mode >= 1 << 5;
            if (private) mode >>= 5;
            log_error("%s %s %u %s", ERROR_PREFIX, "Unsupported screen mode: ", mode, private ? "(private)" : "");
    }
#undef SIMPLE_MODE
#undef MOUSE_MODE
}

View on GitHub (pinned to 6d5d0c4406)

Solutions

  1. Usually harmless - rendering continues without synchronization
  2. If you author the app, only send CSI ? 2026 l after a successful h
  3. Keep frame output under ~250ms so pending mode does not expire
  4. Update the app - duplicate-pending bugs get fixed over time

Example fix

# before
printf '\033[?2026h'; printf '\033[?2026h'  # duplicate begin
# after
printf '\033[?2026h'  # begin once
printf '\033[?2026l'  # end once
Defensive patterns

Strategy: validation

Validate before calling

# track pending-mode state in your renderer
if not pending_active:
    sys.stdout.write('\033[?2026h'); pending_active = True
# ... draw frame ...
sys.stdout.write('\033[?2026l'); pending_active = False

Prevention

When it happens

Trigger: Program using synchronized output sends a duplicate CSI ? 2026 h (begin-begin) or an l after the pending period already expired.

Common situations: Frame-synchronization bugs in TUI apps, or slow rendering causing kitty's pending timeout to expire before the app ends the frame.

Related errors


AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27). Data as JSON: /api/errors/2e10163948e71c93. Report an issue: GitHub.