appsmithorg/appsmith · error · ActionCalledInSyncFieldError

Please remove any direct/indirect references to {{actionName

Error message

Please remove any direct/indirect references to {{actionName}} and try again. Data fields cannot execute framework actions.

What it means

Thrown by the isAsyncGuard wrapping every framework action exposed to the evaluation worker (e.g. showAlert, navigateTo, storeValue, setInterval/setTimeout overrides, geolocation, and setter methods wired through getFnWithGuards). Appsmith classifies bindings into data fields (self.$isDataField === true: properties that merely store data, such as Input.defaultText, Table.tableData, or a plain JS object property) and action fields (onClick, onRowSelect, JS object functions). When an action-wrapped function is invoked while $isDataField is true, the guard sets self.$isAsync = true and throws ActionCalledInSyncFieldError(actionName), whose message substitutes {{actionName}} with fnName + '()'. The rule exists because data fields are evaluated synchronously and must stay free of side effects.

Source

Thrown at app/client/src/workers/Evaluation/fns/utils/fnGuard.ts:37

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    value: function (...args: any[]) {
      const fnWithGuards = getFnWithGuards(fn, fnName, fnGuards);

      return fnWithGuards(...args);
    },
    enumerable: false,
    writable: true,
    configurable: true,
  });
}

export function isAsyncGuard<P extends ReadonlyArray<unknown>>(
  fn: (...args: P) => unknown,
  fnName: string,
) {
  if (self.$isDataField) {
    self["$isAsync"] = true;
    throw new ActionCalledInSyncFieldError(fnName);
  }
}

export function getFnWithGuards(
  // TODO: Fix this the next time the file is edited
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  fn: (...args: any[]) => unknown,
  fnName: string,
  fnGuards: FnGuard[],
) {
  // TODO: Fix this the next time the file is edited
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  return (...args: any[]) => {
    for (const guard of fnGuards) {
      guard(fn, fnName);
    }

    return fn(...args);

View on GitHub (pinned to 8cd9021c24)

Solutions

  1. Move the action call into an action/event field (the widget's onClick/onSuccess, or a JS object function) rather than a data field.
  2. If you need the action driven by a value change, bind the action field to run on the onChange event of the source widget instead of computing it inline in the data field.
  3. Replace side-effecting logic in a data field with a pure expression; store the result of async work via storeValue from an action and read that stored value in the data field.
  4. Search the data field binding for the named action (the message includes actionName) and remove every direct/indirect reference to it.

Example fix

// before (Input1.defaultText - a DATA field)
"Welcome " + (function(){ appsmith.actions.showAlert('set'); return user.name })()

// after
// Input1.defaultText (data field, pure):
"Welcome " + user.name
// Input1.onTextChanged (action field):
appsmith.actions.showAlert('name set')
Defensive patterns

Strategy: validation

Validate before calling

// Structural validation: never call action functions inside a data field.
// Data fields (self.$isDataField === true) include widget value/default properties
// and plain JS-object properties. Action functions (wrapped by isAsyncGuard) are
// showAlert, navigateTo, storeValue, setInterval/setTimeout overrides, geolocation,
// and setter methods.
//
// Rule: if a binding only STORES data, keep it a pure expression; if it must DO
// something, move it into an action/event field (onClick, onSuccess, a JS fn).

Type guard

// There is no safe runtime call to make here: the guard fires at invocation.
// The 'type guard' is structural - know which binding is a data field vs action:
const ACTION_NAMES = new Set([
  'showAlert', 'navigateTo', 'storeValue', 'removeValue', 'clearStore',
  'download', 'copyToClipboard', 'resetWidget', 'setInterval', 'setTimeout',
  'clearInterval', 'clearTimeout',
]);
const isFrameworkAction = (name) => ACTION_NAMES.has(name);
// Never reference isFrameworkAction(name) inside a data field binding.

Try / catch

// try/catch will NOT make an action legal in a data field - it only suppresses
// the guard. The correct pattern is to relocate the call:
//
// BAD (data field Input1.defaultText):
//   (appsmith.actions.showAlert('x'), user.name)
// GOOD:
//   Input1.defaultText -> user.name
//   Input1.onTextChanged -> appsmith.actions.showAlert('x')

Prevention

When it happens

Trigger: Writing appsmith.actions.showAlert('hi') or navigateTo(url) inside a data property such as Input1.defaultText, Table1.tableData, or a non-function property of a JS object; calling setInterval/setTimeout or appsmith.geolocation.getCurrentPosition() in a data field; indirectly referencing an action inside an IIFE or helper that runs during data-field evaluation.

Common situations: New users assuming every binding can trigger actions; refactoring an onClick handler into a default value; calling a JS object function that internally invokes an action, from within a data field; copy-pasting a workflow snippet into the wrong property.

Related errors


AI-assisted analysis of appsmithorg/appsmith@8cd9021c24 (2026-08-12). Data as JSON: /api/errors/a15c71ac2d2b88be. Report an issue: GitHub.