logic.code component’s code is executed with its builtins replaced: the sandbox’s __builtins__ holds only
__build_class__ and __name__, and everything else it offers is bound as an ordinary global before the code
runs. There is no __import__, so any import statement that survives into deployed code raises
ImportError: __import__ not found at execution time.
That is the whole reason the compiler exists, and this page is its ground truth: what
is already bound decides which imports it may drop, which names your modules must not redefine, and which
imports it has to refuse.
What is pre-bound
16 standard-library modules, bound as module objects:typing, Union, Literal.
Exceptions: Exception, ValueError, IndexError, plus the six session exceptions —
GraphMutationConflict, GraphTraversalLimitExceeded, InvalidGraphQuery, InvalidSessionObjectAlias,
InvalidThreadEnvelope, InvalidGraphEnvelope.
59 built-in functions, re-exposed by name since __builtins__ no longer carries them:
Plus three deprecated aliases still bound for backward compatibility, and still a silent-collision hazard:
post_event, get_input_value, set_output_value.
Code Component documents what each of these does. This page is
concerned only with the fact that they are bound.
What is not bound
sys, os, subprocess, requests, open, eval, exec.
None of them can be reached by importing, either — there is no __import__. Code needing any of them belongs in
an external worker rather than in a logic.code component.
Names your code must not redefine
Everything above is an ordinary global in one namespace, so a top-level definition in your code simply replaces it for everything that runs afterwards. That never raises. The platform does not read your globals back — it reads the objects it bound — so redefiningset_result means your function is called instead of the
platform’s, and redefining result means the platform still collects its own dict while your writes go nowhere.
Either way the component completes and produces nothing, with no plausible cause anywhere in the trace.
The compiler refuses this up front as SANDBOX_NAME_SHADOWED, over 109 reserved names: the 59 builtins, the
platform names above, the typing names and the exception names.
One gap worth knowing: the 16 stdlib module names are not in that reserved set. A module defining a top-level
json — a function, a class, an assignment — compiles without complaint and shadows the pre-bound json module
for every other inlined module in the same namespace. The related case that is caught is a local module file
shadowing one, which the compiler resolves local-first and reports as the LOCAL_SHADOWS_SANDBOX_MODULE
advisory.
Two gates before any of this runs
The platform checks both before executing a component:content[]must be non-empty andcontent[0]["type"]must be"text".- The
languageport must resolve topython. An absent or empty port defaults topythonplatform-side; the CLI is stricter and refuses acodebinding whose component has nolanguageport at all, asLANGUAGE_NOT_PYTHON. Writing code into a component that cannot execute it is precisely the failure worth catching at build time.
content[] are then concatenated, each joined with a leading newline, and any part that is not
type: "text" is skipped. So every part must be whole statements and never a partial line — which is why the
compiler emits exactly one part per module.
Extending the set: workflow_component_code_imports
A blob limit listing the third-party modules a revision’s code may import. Each name is imported and merged into
the execution namespace under its literal name from the list, and the compiler treats those names as droppable
exactly like the pre-bound ones.
Reading it is the CLI’s business as much as the platform’s, and it has three sharp edges.
Reading it requires an admin key. The read and write keys the platform recommends for automation get a
403. So the compiler falls back to a local cache, and when it has none, an unresolved import degrades from the
hard failure IMPORT_UNAVAILABLE to the advisory IMPORT_UNVERIFIED — the build proceeds and says it could not
verify. --strict-imports on build and deploy restores the refusal.
A dotted entry binds a key that is not an identifier. numpy.linalg binds under the literal key
"numpy.linalg", which no Python expression can name, so it is unreachable from sandboxed code. The CLI reports
this as the ALLOWLIST_ENTRY_UNUSABLE advisory. It still has a use: importing it is what makes the submodule
reachable as an attribute of an also-allowlisted parent.
An entry colliding with a pre-bound name shadows it. The merge puts the allowlist last, so an entry named
json or re replaces the stdlib binding for every component on the blob. The CLI reports this as
ALLOWLIST_SHADOWS_SANDBOX.
There is a fourth consequence the CLI cannot advise on, because it depends on the deployed environment rather
than the list: the platform imports the whole list inside one try, so a name that fails to import aborts the
loop and every module after it is missing too, with nothing said at execution time. Keeping the list short and
verified is the only defence.
The list is resolved when an execution is created and shipped with the queued message, so editing the limit does
not affect executions already queued.
The local cache
build never talks to the platform, so it classifies against ~/.blobhub/cache/allowlist/, keyed on the
manifest’s own blob: reference — lower-cased, and org-qualified exactly when the manifest itself qualifies it.
A cache entry older than seven days raises the ALLOWLIST_STALE advisory; no entry at all raises
ALLOWLIST_UNREADABLE.
Two commands write that cache, both needing an admin key:
deploy, which refreshes it live on every run, and
blobhub blob limits --refresh, which is how an operator seeds it for builds running under a
key that cannot read limits.
Because the key is the reference as written, a manifest saying blob: checkout and a refresh run against
acme-corp/checkout land on different cache entries. Qualifying the manifest’s blob: with its org avoids that
— and avoids the collision between identically-named blobs in different orgs, which share one entry under a bare
name.
Every advisory named here, with its remediation, is in Error codes.
See also
- Compiler — how these bindings decide what may be dropped, rewritten or refused.
blobhub blob limits— reading the allowlist, and the cache--refreshwrites.- Code Component — what each bound helper does.
- Concepts — the allowlist among the four platform behaviours that shape the binary.

