Problem
When a page contains only stateless livecomponents (i.e. no LiveComponent subclass), calling a command on any of them always returns HTTP 410 Gone.
The root cause is a gap between how sessions are established and how they are checked:
StatelessLiveComponent.get_or_create_state() returns a StatelessModel() directly without writing anything to the state store.
StatelessLiveComponent.set_state() is a no-op for the same reason — stateless components intentionally have no persistent state.
- The
call_command view guards every request with state_manager.session_exists(session_id), which checks for the presence of the Redis hash key lc:states:{session_id}.
- Because no state was ever written, that key does not exist →
session_exists() returns False → the view returns 410 before the command is ever dispatched.
This means stateless components that use commands are entirely broken in isolation — they only work incidentally when a stateful component happens to be on the same page (and therefore creates the session key).
Proposed fix
Add a get_or_create_session mechanism that writes a lightweight sentinel entry to the state store during get_or_create_state of StatelessLiveComponent. This makes the session discoverable without storing any real state:
livecomponents/manager/stores.py — add to IStateStore and both implementations:
# RedisStateStore
def get_or_create_session(self, session_id: str) -> None:
key_name = self._get_key_name(self.key_prefix, session_id)
with self.client.pipeline() as pipe:
pipe.hsetnx(key_name, "__stateless__", b"1")
pipe.expire(key_name, self.ttl)
pipe.execute()
# MemoryStateStore
def get_or_create_session(self, session_id: str) -> None:
sentinel = StateAddress(session_id=session_id, component_id="__stateless__")
self._store.setdefault(sentinel, b"")
livecomponents/manager/manager.py:
def get_or_create_session(self, session_id: str) -> None:
self.store.get_or_create_session(session_id)
livecomponents/component.py — override in StatelessLiveComponent:
def get_or_create_state(self, state_manager, state_addr, request, component_kwargs):
state_manager.get_or_create_session(state_addr.session_id)
return StatelessModel()
This approach keeps TTL enforcement intact, requires no changes to the view layer, and has no effect on purely stateful pages (the sentinel write is idempotent via hsetnx).
Problem
When a page contains only stateless livecomponents (i.e. no
LiveComponentsubclass), calling a command on any of them always returns HTTP 410 Gone.The root cause is a gap between how sessions are established and how they are checked:
StatelessLiveComponent.get_or_create_state()returns aStatelessModel()directly without writing anything to the state store.StatelessLiveComponent.set_state()is a no-op for the same reason — stateless components intentionally have no persistent state.call_commandview guards every request withstate_manager.session_exists(session_id), which checks for the presence of the Redis hash keylc:states:{session_id}.session_exists()returnsFalse→ the view returns 410 before the command is ever dispatched.This means stateless components that use commands are entirely broken in isolation — they only work incidentally when a stateful component happens to be on the same page (and therefore creates the session key).
Proposed fix
Add a
get_or_create_sessionmechanism that writes a lightweight sentinel entry to the state store duringget_or_create_stateofStatelessLiveComponent. This makes the session discoverable without storing any real state:livecomponents/manager/stores.py— add toIStateStoreand both implementations:livecomponents/manager/manager.py:livecomponents/component.py— override inStatelessLiveComponent:This approach keeps TTL enforcement intact, requires no changes to the view layer, and has no effect on purely stateful pages (the sentinel write is idempotent via
hsetnx).