D4Vinci/Scrapling · error · ValueError
Session '{session_id}' is a '{entry.session_type}' session,
Error message
Session '{session_id}' is a '{entry.session_type}' session, but this tool requires a '{expected_type}' session. Use the matching fetch tool for your session type. What it means
Sessions are typed (e.g. 'dynamic' vs 'stealthy') and tools that act on a session validate the type via _get_session(expected_type=...). Using a tool meant for one session type against the other raises this ValueError instead of silently running the wrong fetcher. Pass expected_type=None (as the screenshot tool does) to operate on any session type.
Source
Thrown at scrapling/core/ai.py:169
to the streamable-http transport.
"""
self._sessions: Dict[str, _SessionEntry] = {}
self._executable_path = executable_path or environ.get(MCP_EXECUTABLE_PATH_ENV) or None
self._auth_token = auth_token or environ.get(MCP_AUTH_TOKEN_ENV) or None
def _resolve_executable_path(self, executable_path: Optional[str]) -> Optional[str]:
"""Return a per-call executable path or the server-wide default."""
return executable_path or self._executable_path
def _get_session(self, session_id: str, expected_type: Optional[SessionType]) -> _SessionEntry:
"""Look up a session by ID, optionally validating its type. Pass `None` to skip the type check."""
entry = self._sessions.get(session_id)
if entry is None:
raise ValueError(f"Session '{session_id}' not found. Use list_sessions to see active sessions.")
if not entry.session._is_alive:
raise ValueError(f"Session '{session_id}' is no longer alive. Open a new session.")
if expected_type is not None and entry.session_type != expected_type:
raise ValueError(
f"Session '{session_id}' is a '{entry.session_type}' session, but this tool requires a "
f"'{expected_type}' session. Use the matching fetch tool for your session type."
)
return entry
async def open_session(
self,
session_type: SessionType,
session_id: Optional[str] = None,
headless: bool = True,
google_search: bool = True,
real_chrome: bool = False,
wait: int | float = 0,
proxy: Optional[str | Dict[str, str]] = None,
timezone_id: str | None = None,
locale: str | None = None,
extra_headers: Optional[Dict[str, str]] = None,
useragent: Optional[str] = None,View on GitHub (pinned to 5d213a2d47)
Solutions
- Use the fetch tool that matches the session_type you opened (dynamic tool for dynamic sessions, stealthy tool for stealthy sessions)
- Or close and reopen the session with the type your tool requires
- Call list_sessions to confirm each session's type before dispatching
Example fix
# before sid = (await open_session(session_type='dynamic', ...)).session_id await fetch_with_stealthy_session(session_id=sid, url=url) # after sid = (await open_session(session_type='stealthy', ...)).session_id await fetch_with_stealthy_session(session_id=sid, url=url)
Defensive patterns
Strategy: validation
Validate before calling
sessions = {s.session_id: s.session_type for s in await list_sessions()}
if sessions.get(sid) != 'stealthy':
raise RuntimeError(f'{sid} is {sessions.get(sid)}; open a stealthy session for this tool') Type guard
def tool_matches_session(tool_name: str, session_type: str) -> bool:
return session_type in tool_name # 'dynamic' in fetch_with_dynamic_session, etc. Prevention
- Derive the session_type from the tool you plan to call (and vice versa) instead of choosing independently
- Record the intended tool type alongside each stored session_id
When it happens
Trigger: Opening a session with session_type='dynamic' and then calling the stealthy-specific fetch tool (or vice versa), e.g. fetch_with_stealthy_session on a dynamic session. Each fetch tool is bound to its engine, so the mismatch is rejected up front.
Common situations: LLM agents mixing up tool names after opening a cheaper dynamic session, or a workflow that switched from StealthyFetcher to DynamicFetcher for speed but kept calling the old tool.
Related errors
- Session '{session_id}' not found. Use list_sessions to see a
- Session '{session_id}' is no longer alive. Open a new sessio
- Session '{session_id}' already exists. Use a different ID or
- Credentials dictionary must contain both 'username' and 'pas
- 'quality' is only valid when 'image_type' is 'jpeg'.
AI-assisted analysis of D4Vinci/Scrapling@5d213a2d47 (2026-08-14).
Data as JSON: /api/errors/a1f32e69cee19107.
Report an issue: GitHub.