Skip to content

Fix/anywidget refresh value#9454

Open
Shamik-07 wants to merge 10 commits intomarimo-team:mainfrom
Shamik-07:fix/anywidget_refresh_value
Open

Fix/anywidget refresh value#9454
Shamik-07 wants to merge 10 commits intomarimo-team:mainfrom
Shamik-07:fix/anywidget_refresh_value

Conversation

@Shamik-07
Copy link
Copy Markdown

@Shamik-07 Shamik-07 commented May 4, 2026

📝 Summary

The Counter anywidget was always defaulting to a default value of 5 upon reloading the notebook as the state wasn't being reattached to the FE. This fixes the issue by sending an echo_update to BE and upon reload reconnects to get the correct state. FE calls reemitState ensure that all listeners receive the state even if one was rendered before the other.
Closes #9420

Screen.Recording.2026-05-04.at.17.29.29.mov

📋 Pre-Review Checklist

  • For large changes, or changes that affect the public API: this change was discussed or approved through an issue, on Discord, or the community discussions (Please provide a link if applicable).
  • Any AI generated code has been reviewed line-by-line by the human PR author, who stands by it.
  • Video or media evidence is provided for any visual changes (optional).

✅ Merge Checklist

  • I have read the contributor guidelines.
  • Documentation has been updated where applicable, including docstrings for API changes.
  • Tests have been added for the changes made.

@vercel
Copy link
Copy Markdown

vercel Bot commented May 4, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview, Comment May 7, 2026 9:22pm

Request Review

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 6 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="marimo/_plugins/ui/_impl/comm.py">

<violation number="1" location="marimo/_plugins/ui/_impl/comm.py:123">
P3: Update `_create_model_message` docstring: it still says `echo_update` is skipped, but this branch now emits a `ModelUpdate`.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant UI as Widget UI (FE)
    participant Plugin as AnyWidgetPlugin
    participant Model as Frontend Model
    participant Comm as Marimo Comm (BE)
    participant Kernel as Python Kernel

    Note over UI, Kernel: Phase 1: State Capture (During Interaction)

    UI->>Model: model.set("count", 8)
    Model->>Comm: sendUpdate({ method: "echo_update", state: {count: 8} })
    
    rect rgb(240, 240, 240)
        Note over Comm: CHANGED: Handle "echo_update"
        Comm->>Comm: Create ModelUpdate message
        Note right of Comm: Prevents dropping state<br/>on acknowledgement path
    end
    
    Comm-->>Kernel: Broadcast state to all clients

    Note over UI, Kernel: Phase 2: State Hydration (Upon Notebook Reload)

    Kernel->>Comm: Replay stored state
    Comm->>Model: updateAndEmitDiffs({ count: 8 })
    
    Plugin->>Plugin: bind(widgetDef, model)
    Plugin->>UI: render(element, model)
    
    alt Widget Initialization
        UI->>Model: model.on("change:count", listener)
        Note left of UI: Widget defaults to "5"<br/>(Internal initial state)
    end

    rect rgb(240, 240, 240)
        Note over Plugin, Model: NEW: Re-hydration Flow
        Plugin->>Model: getMarimoInternal(model).reemitState()
        
        loop For each key in state
            Model->>UI: emit("change:count", 8)
        end
        
        Model->>UI: emit("change")
    end

    UI->>UI: listener updates UI to "8"
Loading

Partial review: This PR has more than 50 files, so cubic reviewed the highest-priority files first. During the trial, paid plans get a higher file limit.
You can try an ultrareview to bypass the file limit, comment @cubic-dev-ai ultrareview. Learn more.
Fix all with cubic.

elif method == "echo_update":
# echo_update is for multi-client sync acknowledgment, skip it
return None
# Preserve frontend-driven trait changes for reconnect replay.
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot May 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Update _create_model_message docstring: it still says echo_update is skipped, but this branch now emits a ModelUpdate.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At marimo/_plugins/ui/_impl/comm.py, line 123:

<comment>Update `_create_model_message` docstring: it still says `echo_update` is skipped, but this branch now emits a `ModelUpdate`.</comment>

<file context>
@@ -120,8 +120,14 @@ def _create_model_message(
     elif method == "echo_update":
-        # echo_update is for multi-client sync acknowledgment, skip it
-        return None
+        # Preserve frontend-driven trait changes for reconnect replay.
+        # anywidget/ipywidgets can emit echo_update as the synchronisation
+        # acknowledgement path; dropping it causes stale replay state.
</file context>
Fix with Cubic

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes anywidget model state hydration on browser refresh/reconnect by ensuring frontend-originated state changes are preserved for session replay and by re-emitting the current model state after a widget view attaches listeners.

Changes:

  • Backend: treat echo_update messages as ModelUpdate so they are included in replayable model state.
  • Frontend: add an internal reemitState() API to re-dispatch current model values as change:* events after render().
  • Tests: add backend and frontend test coverage for echo_update replay contribution and late listener hydration.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
marimo/_plugins/ui/_impl/comm.py Converts echo_update into ModelUpdate to preserve state for reconnect replay.
tests/_plugins/ui/_impl/test_comm.py Adds coverage ensuring echo_update is broadcast as a state-bearing update.
frontend/src/plugins/impl/anywidget/model.ts Adds internal reemitState() that emits change:* events for current state.
frontend/src/plugins/impl/anywidget/AnyWidgetPlugin.tsx Calls reemitState() after render() so late listeners observe hydrated state.
frontend/src/plugins/impl/anywidget/__tests__/model.test.ts Tests reemitState() emits field and aggregate change events.
frontend/src/plugins/impl/anywidget/__tests__/AnyWidgetPlugin.test.tsx Adds regression test for hydration when listener attaches after initial state is set.

Comment on lines 122 to +126
elif method == "echo_update":
# echo_update is for multi-client sync acknowledgment, skip it
return None
# Preserve frontend-driven trait changes for reconnect replay.
# anywidget/ipywidgets can emit echo_update as the synchronisation
# acknowledgement path; dropping it causes stale replay state.
return ModelUpdate(
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

anywidget model.get() returns default value in render() on browser refresh

2 participants