Skip to content

Migrated composer: 4th+ reference never attaches — @ typed at a caret wedged behind the 3rd chip #997

Open

Description

@Ltwoz

Summary

On flow.google.com (migrated host), image generation with more than 3 references fails: chips 1–3
attach, the 4th never does. The model accepts up to 10 (gflow models lists nano2 with a reference
cap of 10), so the limit is not Flow's. The cause is where the caret lands when gflow clicks the
composer before typing @.

Environment

  • gflow-cli 0.83.1
  • Host: flow.google.com (migrated composer)
  • Model: nano2 (Nano Banana 2.1), aspect 9:16
  • macOS, headed Chrome (default transport)

Symptoms

  • In-place refs (GenerateImageRequest.refs with ImageRef(in_project=True)):
    _mention_by_token raises ReferenceNotFoundError for the 4th ref.
  • Uploaded refs (4+ local --ref files): _mention_by_name fails with exit 32,
    clearing the failed 'ref-…' query removed an already-attached reference (3 chip(s) before, 0 after).
    This path uses the same composer click, so it is very likely the same cause; the fix below was
    verified on the in-place path only.
  • Reproducible every time (3 runs, ref order shuffled): whichever image is 4th fails; 1–3 always attach.
  • After the failure, the composer contains the search text as plain text right after the 3rd chip,
    and no picker was opened.

Repro

  1. Create a project on flow.google.com and upload 4 reference images.
  2. Generate an image that references all 4 (in-place, or gflow image i2i with 4 --ref files).
  3. Watch the composer: chips 1–3 attach; for the 4th, @ is typed but no picker opens, and the
    query lands as plain text.

Root cause

Before every @, both MigratedComposer._mention_by_token and MigratedComposer._mention_by_name
(gflow_cli/api/transports/migrated_composer.py) do:

await page.locator(COMPOSER).first.click(timeout=5000)

Playwright clicks the center of the ProseMirror editor. With 3 chips the first line is full and
the trailing space wraps to line 2, so a center click puts the caret directly after the 3rd chip,
with no space before it
. ProseMirror's mention plugin does not open its picker for an @ typed
there, so the @ is swallowed and the query becomes plain text. With 1–2 chips the center click
happens to land after the trailing space, which is why the first three work.

Pressing End does not help: it moves to the end of the visual line, not the end of the document.

Proposed fix

After clicking the composer in _mention_by_token and _mention_by_name, move the selection to the
end of the document before typing @:

await page.locator(COMPOSER).first.click(timeout=5000)
await page.evaluate(
    """(sel) => {
      const ce = document.querySelector(sel); if (!ce) return;
      ce.focus();
      const r = document.createRange(); r.selectNodeContents(ce); r.collapse(false);
      const s = window.getSelection(); s.removeAllRanges(); s.addRange(r);
    }""",
    COMPOSER,
)
await page.wait_for_timeout(300)
await page.keyboard.type("@", delay=120)

(A ProseMirror-native alternative would be dispatching a TextSelection.atEnd(doc) transaction, but
the editor view is not exposed on the page; the DOM Range approach works because ProseMirror syncs
its selection from the DOM on selectionchange.)

Verified

Applied as a runtime wrapper around those two methods on 0.83.1:

  • 10 in-place references attached, 4/4 runs.
  • 1 real generation with the fix applied succeeded.

clear_composer and send_prompt were left as-is; only the two mention paths need it.

Activity

  1. ffroliva commented on Oct 19, 2026

    @ffroliva
    Owner

    Assessment of #997: LIKELY-BUG / NEEDS-E2E (confidence 7/10)

    Thanks for the precise write-up, @Ltwoz. We measured it on our side before acting on it. This was on a flow.google.com account, Windows, headed Chrome, a 1280×720 viewport, calling the production _mention_by_token unchanged, with nothing submitted.

    What we reproduced (3/3 runs): the caret wedge is real. With 3 chips in the composer, the centre click lands on a chip and the caret stops right behind chip 2. The click is at migrated_composer.py in _mention_by_token and _mention_by_name; the same click is also in _mention_character.

    What we did not reproduce: on our setup the picker still opened every time (60/60 mentions), and all references attached (5/5). The 4th one went in out of order, so the chips read 1, 2, 4, 3. The chip guards count chips but don't check their order, so nothing reports the misorder. A chip right before the caret did not stop the picker opening here, so the "no picker" half of the root cause is still unexplained.

    Your fix works: moving the selection to the end of the composer before each @ gave the correct order (1, 2, 3, 4) in 3/3 runs. We haven't yet tested the wrapped-chip-line case you mention, where End no longer helps.

    Next:

    • We're fixing the misorder now, using a caret-to-end placement at all three click sites plus a guard that checks chip order. We'll link it with Refs #997.
    • To pin down the no-picker failure you saw, could you share:
      1. your browser window size (or window.innerWidth × innerHeight) and macOS version;
      2. right after the composer click that comes before the 4th @, the composer's innerHTML and getSelection().anchorNode / anchorOffset.
  2. added 2 commits that reference this issue on Oct 19, 2026
  3. ffroliva commented on Oct 19, 2026

    @ffroliva
    Owner

    Merged #1002 to develop (ships next release). gflow now moves the caret to the end of the prompt before every @, at all three mention sites: in-place refs, uploaded --ref files and @Character. A mention that lands out of order now fails with exit 32 before submit, instead of passing silently. Our e2e: develop read chips [1, 2, 4, 3]; the fix reads them in order (1 passed, $0, nothing submitted).

    Keeping this open for the other half you reported: on macOS the picker never opens for the 4th reference. That didn't reproduce here (60/60 pickers opened at 1280×720 on Windows). When you get a chance, please try the next release and, if it still happens, share the window size and the composer innerHTML / selection anchor right before the 4th @. Thanks again for the diagnosis; your Range-to-end fix is what shipped.

  4. added 4 commits that reference this issue on Oct 20, 2026
  5. ffroliva commented on Oct 23, 2026

    @ffroliva
    Owner

    @Ltwoz a follow-up measurement on the half that was still open (no picker for the 4th+ reference). It's from 2026-10-23, on develop as released in v0.89.0: a flow.google.com account, Windows, headed Chrome.

    I forced the wrap you described by narrowing the window to 900 px. The composer was 565 px wide, and chips moved onto a second line from the 5th mention.

    So the mechanism you identified is handled here even with the wrap. If the picker still fails to open on macOS with v0.89.0 (gflow update), please share:

    • the window size;
    • the composer innerHTML right before the failing @;
    • the exit code you get. A misplaced mention now fails loudly with exit 32 before submit, rather than going in out of order.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions