huggingface/smolagents · error · ImportError

No active IPython shell found

Error message

No active IPython shell found

What it means

When inspect.getsource fails (e.g. object defined in an interactive session), get_source falls back to searching IPython history. If IPython imports but IPython.get_ipython() returns None — no live shell in this process — this ImportError is raised, then re-raised as the original inspect error by the except ImportError handler.

Source

Thrown at src/smolagents/utils.py:412

        TODO: handle Python standard REPL
    """
    if not (isinstance(obj, type) or callable(obj)):
        raise TypeError(f"Expected class or callable, got {type(obj)}")

    inspect_error = None
    try:
        # Handle dynamically created classes
        source = getattr(obj, "__source__", None) or inspect.getsource(obj)
        return dedent(source).strip()
    except OSError as e:
        # let's keep track of the exception to raise it if all further methods fail
        inspect_error = e
    try:
        import IPython

        shell = IPython.get_ipython()
        if not shell:
            raise ImportError("No active IPython shell found")
        all_cells = "\n".join(shell.user_ns.get("In", [])).strip()
        if not all_cells:
            raise ValueError("No code cells found in IPython session")

        tree = ast.parse(all_cells)
        for node in ast.walk(tree):
            if isinstance(node, (ast.ClassDef, ast.FunctionDef)) and node.name == obj.__name__:
                return dedent("\n".join(all_cells.split("\n")[node.lineno - 1 : node.end_lineno])).strip()
        raise ValueError(f"Could not find source code for {obj.__name__} in IPython history")
    except ImportError:
        # IPython is not available, let's just raise the original inspect error
        raise inspect_error
    except ValueError as e:
        # IPython is available but we couldn't find the source code, let's raise the error
        raise e from inspect_error


def encode_image_base64(image):

View on GitHub (pinned to 30bb116109)

Solutions

  1. Define the tool in a real .py module so inspect.getsource works
  2. Attach a __source__ attribute to dynamically created tools
  3. Run the serialization in the same process/session where the object was defined
  4. When in notebooks, run the defining cell in the same kernel before serializing

Example fix

# before
# tool defined with exec in a worker -> get_source fails

# after
# my_tools.py
def search(query: str) -> str:
    ...
search_tool = tool(search)
Defensive patterns

Strategy: fallback

Validate before calling

import inspect
def has_recoverable_source(obj) -> bool:
    try:
        inspect.getsource(obj)
        return True
    except (OSError, TypeError):
        return bool(getattr(obj, '__source__', None)) or _ipython_available_with_history()

Type guard

def can_get_source(obj) -> bool:
    import inspect
    try:
        inspect.getsource(obj)
        return True
    except Exception:
        return getattr(obj, '__source__', None) is not None

Try / catch

try:
    src = get_source(obj)
except Exception as e:
    if 'No active IPython' in str(e) or isinstance(e, OSError):
        src = getattr(obj, '__source__', None) or fallback_repr(obj)

Prevention

When it happens

Trigger: Serializing a tool/class that was defined in a REPL/notebook-in-another-process: inspect has no source file, and get_source runs where no IPython shell exists (plain script, worker process).

Common situations: Using @tool in a subprocess or detached worker after pickling from a notebook; reloading modules so inspect loses file mappings; objects created via exec without __source__.

Related errors


AI-assisted analysis of huggingface/smolagents@30bb116109 (2026-08-28). Data as JSON: /api/errors/16b9217648b7fe9b. Report an issue: GitHub.