On Wed, Sep 09, 2026 at 06:09:05PM +0100, José Matos wrote: > It is that time of the year again. :-)
.. before I grab my coffee ... ;) Claude Code v2.1.236 $ check it out: https://www.mail-archive.com/[email protected]/msg226791.html * Fetch(https://www.mail-archive.com/[email protected]/msg226791.html) -> Received 9.6KB (200 OK) Ran 20 shell commands * Read it. Short version: that assert cannot happen on master - it is 2.5.x-only, and the culprit is the IM acceptance hook, not the paste. Mechanism GuiApplication::processKeySym() sets command_phase_ and emits keyCommandStateSet() before dispatching the key. That signal is connected to every GuiWorkArea (GuiWorkArea::init(), GuiWorkArea.h:107 re-emits it to its own GuiInputMethod). So one keystroke anywhere makes every open pane/tab query its own BufferView cursor. In 2.5.x toggleInputMethodAcceptance() still carries the pre-Qt-6.11 workaround: if (guiApp->platformName() != "cocoa") d->im_off_in_math_ = d->cur_->getFont().language()->inputMethodOffInMath(); Cursor::getFont() indexes paragraph data (text.getPar(sl.pit()), par.getFont(..., pos, ...)). A cursor that has not been sanitized since the last edit is exactly what José hit: pos: 33 size: 1. The function's own next comment already admits the cursor is a lagging cache - but inset() only touches the slice, while getFont() walks the fontlist. Master dropped that block in 2ca8f53038 (Yokota, 2026-06-20, "Remove an old workaround" - QInputMethod::locale() now works on Linux), which is not in 2.5.x. Hence 2.5.3 asserts and 2.6 does not. Worth noting the latent worse case: if the stale pit_ is out of range instead of just pos, there is no assert at all - getPar(pit) is a plain operator[] on the paragraph vector. Same family as the closed #6896 / #7694 split-view asserts, resurrected by the new per-keypress broadcast. Minimal 2.5.x fix (avoid paragraph indexing; use the cursor's cached font): --- a/src/frontends/qt/GuiInputMethod.cpp +++ b/src/frontends/qt/GuiInputMethod.cpp @@ -134,13 +134,16 @@ void GuiInputMethod::toggleInputMethodAcceptance() { if (guiApp->platformName() != "cocoa") // Since QInputMethod::locale() doesn't work on other systems, use // language at the cursor point to infer the current input language // as a second best - d->im_off_in_math_ = - d->cur_->getFont().language()->inputMethodOffInMath(); + // Do not use Cursor::getFont() here: this runs for every work + // area on every key press, before dispatch, so a cursor that + // has not been sanitized yet can point outside its paragraph. + d->im_off_in_math_ = + d->cur_->real_current_font.language()->inputMethodOffInMath(); // note that d->cur_->inset() is a cache so it lags from actual key moves bool enabled = !((d->im_off_in_math_ && d->cur_->inset().currentMode() == Inset::MATH_MODE) || guiApp->isInCommandMode()); The alternative is to just backport 2ca8f53038 to 2.5.x, which removes the call outright; the cost is relying on QInputMethod::locale() for the older Qt versions 2.5.x still supports (an IM-in-math behaviour regression, not a crash). I would take the one-liner above for 2.5.3 and leave the workaround removal to master. Want me to try reproducing under the Xvfb harness (split view, multi-paragraph paste at end of paragraph, then any keypress) before you reply on the list? $ yep -> Read 10 files, listed 3 directories, ran 37 shell commands * Reproduced deterministically, and the fix holds. Everything ran headless via ~/.claude/lyx/xvfb-lyx.sh; nothing is left running. Repro (in the repo: im-stale-cursor-test/repro.sh + docA.lyx/docB.lyx, XVFBLYX_LYX=<build>/src/lyx ./repro.sh) 1. Open docB (par 0 = 40 chars, par 1 = "y"); view-split - now docB has two work areas. 2. In pane 2, move the cursor to par 0, pos 10, then switch that pane's tab to docA. docB's pane-2 work area is hidden, its cursor frozen at pos 10. 3. In pane 1 (docB), paste a 2-paragraph clipboard at the start of par 0, whose first paragraph is 1 char. Par 0 is now size 1. 4. Press any key -> processKeySym() broadcasts keyCommandStateSet(), the hidden work area's GuiInputMethod walks its stale cursor into paragraph data, dead. Results, same sequence throughout: ?????????????????????????????????????????????????????????????????????????? ? build ? outcome ? ?????????????????????????????????????????????????????????????????????????? ? 2.5.x (.testbuilds/2.5-fixed, 2.5.0-114) ? crash, twice out of twice ? ?????????????????????????????????????????????????????????????????????????? ? same, no split view (single work area) ? survives ? ?????????????????????????????????????????????????????????????????????????? ? master (src/lyx, 86310d6fcc) ? survives ? ?????????????????????????????????????????????????????????????????????????? ? 2.5.x + the real_current_font one-liner ? survives, keys keep landing ? ?????????????????????????????????????????????????????????????????????????? One correction/addition to what I said earlier. On my build (libstdc++ assertions on) it dies earlier than on José's Fedora build: basic_string.h:1221: ... [with _CharT = wchar_t]: Assertion '__pos <= size()' failed. That is Paragraph::isSeparator() - return d->text_[pos] == ' ';, unguarded - reached from the if (pos > 0) block of Cursor::getFont(), before the LBUFERR in getFontSettings() that José saw. So without _GLIBCXX_ASSERTIONS the first thing this does is a silent out-of-bounds read on the paragraph's docstring; the printed assert is the polite version of it. Worth saying in the reply - it makes it a bit more than a cosmetic assert. Also latent, in both branches: GuiInputMethod::inputMethodQuery()'s Qt::ImPreferredLanguage case still does d->cur_->getFont().language() (2.5.x:1056, master:1148). Only queried for the focused work area, so much less exposed, but it is the same dereference. $ Jose likes emoticons, give him some nice ascii art for good night * Here you go - drop this at the bottom of the reply: * . * . * . . _____ * . * * | | . * . _|_____|_ . * . * * | | . zZz . _|_________|_ * zZz * . * | | zZz . * | L y X | . * . * |_____________| . * . ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Good night, Jose! (-_-) zZz The cursor sleeps too... in another work area. \(^_^)/ <- your paragraph, still size 1 (>_<) <- the stale cursor, pos 33 Sweet dreams, and may your pos never exceed size. ;-) -- Can forward repro docs and script if anyone wants. P(/C) -- lyx-devel mailing list [email protected] https://lists.lyx.org/mailman/listinfo/lyx-devel
