Nubu
  • Features
  • How it works
  • Pricing
  • Integrations
  • FAQ
  • Contact
  • Docs
  • Blog
  • Log in
  • Log inSign up
    Nubu

    Creative automation for teams that ship campaigns, not busywork.

    Try for free
    Product
    FeaturesNubu MotionHow it worksPricingIntegrationsFAQ
    Company
    AboutContactDocsBlogLog in
    Legal
    Privacy policyTerms of serviceData deletion
    © 2026 Bear Studios · Nubu
    Introduction
    Templates overviewInstalling Nubu BuilderAdding template compsAdding propertiesAdding footageSetting defaultsExporting your templateUploading a template
    Flows overviewAvailable nodesAdding a nodeConnecting nodesWorking with dataTemplates and outputsUsing AI nodesBuilding a creative
    CampaignsTemplatesAssetsGlossariesRendersSettings
    Connecting Meta AdsConnecting Google AdsConnecting OpenAIConnecting Gemini AI StudioConnecting Anthropic
    AI Assistant overviewPrompt examplesAssistant settings and controls
    Nubu Motion scripts: the reference
    OverviewQuick startDifferences from After EffectsDeterminism
    JavaScript supportReferences and idsProject scriptsState and timeBudgets and errorsFormatting values
    GlobalTime conversionInterpolationVector mathsRandom and noiseColour conversionOther maths
    LayerCompPropertyKeyframePathGeometryProjectComponentConsoleComponentControlExpressionControl
    The property vocabularyAnimators propertiesAudio propertiesContent propertiesContents propertiesEffects propertiesExpression controls propertiesFills propertiesInstance controls propertiesLayer propertiesLayout propertiesMasks propertiesStrokes propertiesText style propertiesTransform propertiesTrim path properties
    Error codesBudgets
    Changelog
    All docs
    Motion Scripting
    / Errors

    Error codes

    All fifteen typed script failures in one table: what each code means, what triggers it and how to put it right.

    Every script failure is TYPED: one of the 15 codes below, carried on the script's status with a message (and, for cross-script pulls, the chain of who was evaluating whom). Errors never crash the editor or the render - the failing script's outputs fall back to their base values for that frame, the frame still draws, and nothing latches: the next frame evaluates afresh.

    Table of contentsLanguagesyntax_errorno_outputRuntime and budgetsruntime_errorinterruptedWorked example: A runaway loop, caughtstack_overflowout_of_memoryOutputs and typestype_mismatchWorked example: A value the property cannot takeundeclared_outputWorked example: Writing outside the declarationReferences and schedulingreference_missingWorked example: An id that names nothingcycleWorked example: Two scripts reading each otherStatestate_unserialisablestate_too_largememo_time_accessScheduling and policyasync_unsettleddisabled_by_engine

    Language#

    Raised before a single frame runs - the source itself is unusable.

    syntax_error#

    The source did not parse.

    no_output#

    The script exports no frame function / returned undefined for its one output.

    Runtime and budgets#

    Raised while a frame evaluates. The frame still renders with base values; nothing crashes and nothing latches - the next frame evaluates afresh.

    runtime_error#

    The script threw at runtime.

    interrupted#

    The script exhausted its poll budget.

    Worked example: A runaway loop, caught#

    The scene. One orange square resting on a dark 640 by 360 stage.

    • The comp Drift: 640 by 360 at 25 fps, 100 frames (4 seconds).
    • Square: a solid layer, at [320, 180], 60 by 60, filled #f2a833.

    The script. Add a project script named Runaway and paste:

    export const outputs = ["layer_1:transform.opacity"];
    
    export function frame() {
      let n = 0;
      while (true) {
        n += 1;
      }
    }
    

    Line by line:

    • Lines 5-7: The loop never ends - and nothing freezes. The engine counts interpreter polls and stops the script when the budget (200 polls per script per frame) is spent: the status becomes budget with the typed interrupted code, the frame still renders with base values, and the editor stays responsive. The IDE presents it as a fixable budget issue, pointing at the loop. Regular expressions that backtrack forever and runaway recursion are caught by the same net.

    What you see. The editor keeps running: the script's status shows a budget trip with the interrupted code, the square keeps its base look, and nothing anywhere freezes.

    The editor keeps running: the script's status shows a budget trip with the interrupted code, the square keeps its base look, and nothing anywhere freezes.

    stack_overflow#

    The JS call stack guard fired.

    out_of_memory#

    The realm hit its memory limit.

    Outputs and types#

    Raised by writes - the outputs contract and the drive set's boundary.

    type_mismatch#

    A value could not be coerced to the property kind (or the kind is not drivable yet).

    Worked example: A value the property cannot take#

    The scene. One orange square resting on a dark 640 by 360 stage.

    • The comp Drift: 640 by 360 at 25 fps, 100 frames (4 seconds).
    • Square: a solid layer, at [320, 180], 60 by 60, filled #f2a833.

    The script. Add a project script named Colour push and paste:

    export const outputs = ["layer_1:content.fill_colour"];
    
    export function frame() {
      set("layer_1:content.fill_colour", 42);
    }
    

    Line by line:

    • Line 4: Every ANIMATABLE property is writable - fills, strokes, effects, masks, text, the lot - but each takes its own wire shape: a colour is [r, g, b, a] in 0..1 or a "#rrggbb" string, a point is [x, y], a stop list is an array of stops. A bare number cannot become a colour, so the write refuses with the typed type_mismatch and the message names the shape it wanted. The same code fires for a value out of range (an opacity above 100, a trim fraction above 1), for a STATIC key (a path layer's geometry, a stroke's cap, an effect's mode - those change through commands: "the property is static"), and for a value outside a key's rails - an audio level above +12 dB refuses ("Audio level stays within -96..+12 dB.") rather than clamping.

    What you see. The fill stays put: the script errors with type_mismatch and its status names the shape the colour wanted - a typed refusal, never a silent no-op.

    The fill stays put: the script errors with type_mismatch and its status names the shape the colour wanted - a typed refusal, never a silent no-op.

    undeclared_output#

    set() targeted a property outside the script's outputs.

    Worked example: Writing outside the declaration#

    The scene. One orange square resting on a dark 640 by 360 stage.

    • The comp Drift: 640 by 360 at 25 fps, 100 frames (4 seconds).
    • Square: a solid layer, at [320, 180], 60 by 60, filled #f2a833.

    The script. Add a project script named Overreach and paste:

    export const outputs = ["layer_1:transform.opacity"];
    
    export function frame() {
      set("layer_1:transform.rotation", 45);
    }
    

    Line by line:

    • Line 1: The declaration says: this script may drive the Square's opacity, nothing else.
    • Line 4: The set() targets rotation - a property the script never declared. The write refuses with the typed undeclared_output, the whole frame's outputs fall back to their base values, and the script's status chip carries the error. Add the id to outputs (the quick fix offers to) and the same line succeeds.

    What you see. Nothing moves: the script errors with undeclared_output, its status chip explains which target was outside the declaration, and every property keeps its base value.

    Nothing moves: the script errors with undeclared_output, its status chip explains which target was outside the declaration, and every property keeps its base value.

    References and scheduling#

    Raised by reads - missing ids and circular pulls between scripts.

    reference_missing#

    An id named no property, layer or control in scope.

    Worked example: An id that names nothing#

    The scene. One orange square resting on a dark 640 by 360 stage.

    • The comp Drift: 640 by 360 at 25 fps, 100 frames (4 seconds).
    • Square: a solid layer, at [320, 180], 60 by 60, filled #f2a833.

    The script. Add a project script named Ghost read and paste:

    export const outputs = ["layer_1:transform.opacity"];
    
    export function frame() {
      return layer("layer_9").opacity.value;
    }
    

    Line by line:

    • Line 4: No ninth layer exists, so layer() raises the typed reference_missing - never undefined, never a silent NaN. The IDE shows the dangling id in the error tint the moment the layer disappears, and the status chip names the missing id. Deleting a referenced layer is the ONE way to break a reference; renaming never is.

    What you see. The square keeps its base look while the script's status reports reference_missing, naming the id that resolves to nothing.

    The square keeps its base look while the script's status reports reference_missing, naming the id that resolves to nothing.

    cycle#

    A script read a property another script was still computing.

    Worked example: Two scripts reading each other#

    The scene. A teal leader square keyframed to cross the stage left to right, with an orange follower resting below its start point.

    • The comp Leaders: 640 by 360 at 25 fps, 100 frames (4 seconds).
    • Leader: a solid layer, at [80, 140], 40 by 40, filled #2ec4b6.
      • Keyframe transform.position at frame 0: [80, 140] (linear).
      • Keyframe transform.position at frame 96: [560, 140] (linear).
    • Follower: a solid layer, at [80, 240], 40 by 40, filled #f2a833.

    A second script, A reads B, is added first:

    export const outputs = ["layer_1:transform.opacity"];
    
    export function frame() {
      return prop("Follower:transform.opacity").value;
    }
    

    Then the script under test, B reads A:

    export const outputs = ["layer_2:transform.opacity"];
    
    export function frame() {
      return prop("Leader:transform.opacity").value;
    }
    

    Line by line:

    • Line 4: Script A drives the Leader's opacity by reading the Follower's; this script drives the Follower's by reading the Leader's. Evaluation is demand-driven - reading a property another script drives runs that script first - so this read re-enters a script already on the stack. That is the typed cycle: the read falls back to the pre-expression value, the reading script errors for the frame, and the chain in its status shows the loop. Break the loop by feeding one side from something else.

    What you see. Both squares keep their base opacity while one script's status reports the typed cycle, its chain spelling out who was reading whom.

    Both squares keep their base opacity while one script's status reports the typed cycle, its chain spelling out who was reading whom.

    State#

    Raised by the state fold's rules.

    state_unserialisable#

    A state store held a non-plain-data value (state PR).

    state_too_large#

    A state store exceeded its size cap (state PR).

    memo_time_access#

    A memo body read time/frame (state PR).

    Scheduling and policy#

    The frame boundary and the capability gate.

    async_unsettled#

    A returned promise never settled inside the frame.

    disabled_by_engine#

    A capability was withheld.