nodejs/node · error · SecurityError

%r is not safely callable

Error message

%r is not safely callable

What it means

Raised by SandboxedEnvironment.call when the object being invoked from sandboxed template code is not marked as safely callable. is_safe_callable returns True only for callables explicitly approved: marked with @safe or possessing the unsafe_callable attribute unset (default safe), and explicitly excluded when decorated with @unsafe. The SecurityError prevents sandboxed templates from invoking arbitrary Python functions.

Source

Thrown at tools/inspector_protocol/jinja2/sandbox.py:426

        """
        if isinstance(s, Markup):
            formatter = SandboxedEscapeFormatter(self, s.escape)
        else:
            formatter = SandboxedFormatter(self)
        kwargs = _MagicFormatMapping(args, kwargs)
        rv = formatter.vformat(s, args, kwargs)
        return type(s)(rv)

    def call(__self, __context, __obj, *args, **kwargs):
        """Call an object from sandboxed code."""
        fmt = inspect_format_method(__obj)
        if fmt is not None:
            return __self.format_string(fmt, args, kwargs)

        # the double prefixes are to avoid double keyword argument
        # errors when proxying the call.
        if not __self.is_safe_callable(__obj):
            raise SecurityError('%r is not safely callable' % (__obj,))
        return __context.call(__obj, *args, **kwargs)


class ImmutableSandboxedEnvironment(SandboxedEnvironment):
    """Works exactly like the regular `SandboxedEnvironment` but does not
    permit modifications on the builtin mutable objects `list`, `set`, and
    `dict` by using the :func:`modifies_known_mutable` function.
    """

    def is_safe_attribute(self, obj, attr, value):
        if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value):
            return False
        return not modifies_known_mutable(obj, attr)


# This really is not a public API apparenlty.
try:
    from _string import formatter_field_name_split

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Decorate the genuinely safe callable with @jinja2.sandbox.safe so is_safe_callable returns True.
  2. Do not pass the callable into the context; instead expose a vetted wrapper function that performs the operation in Python.
  3. Refactor the template to call only attributes/values, not arbitrary methods; precompute results in Python.
  4. Audit context objects: replace live ORM/manager objects with plain dicts or data-transfer objects.

Example fix

// before
def render(template_str, ctx):
    return SandboxedEnvironment().from_string(template_str).render(**ctx)
render('{{ acct.delete() }}', {'acct': Account(...)})
// after
from jinja2.sandbox import safe
@safe
def delete_account(acct):
    acct.delete()
    return 'deleted'
render('{{ delete_account(acct) }}', {'acct': acct, 'delete_account': delete_account})
Defensive patterns

Strategy: type-guard

Validate before calling

from jinja2.sandbox import SandboxedEnvironment, unsafe
# whitelist: build the context from a dataclass/dict, never from live model objects
ctx = {'user': {'name': acct.name, 'email': acct.email}}  # plain dict only
env = SandboxedEnvironment()
env.from_string(tpl).render(**ctx)

Type guard

from jinja2.sandbox import safe
from functools import wraps

def is_safe_callable(obj) -> bool:
    return callable(obj) and not getattr(obj, 'unsafe_callable', False)

# explicit allow-list decorator
@safe
def approved(fn):
    return fn

Try / catch

from jinja2.exceptions import SecurityError
try:
    env.from_string(tpl).render(**ctx)
except SecurityError as e:
    log.warning('sandbox rejected callable: %s', e)
    html = render_safe_fallback()

Prevention

When it happens

Trigger: Template calls {{ user.delete_account() }} or {{ os.system('rm -rf') }} where the callable was passed into the context but is not approved. Passing a class instance method marked @unsafe into the template context and invoking it. Calling a callable that the sandbox does not recognize as formatted/known-safe.

Common situations: Leaking ORM model methods or framework helpers into the template context. Using SandboxedEnvironment for user-uploaded templates and accidentally exposing privileged callables. Marking too many things @unsafe during a security review and breaking legitimate calls.

Related errors


AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13). Data as JSON: /api/errors/ab140f720243646f. Report an issue: GitHub.