twentyhq/twenty · error · Error

getPublicAssetUrl can only be called from within a logic fun

Error message

getPublicAssetUrl can only be called from within a logic function or front component

What it means

Thrown by getPublicAssetUrl when process.env[DEFAULT_API_URL_NAME] or process.env[DEFAULT_APP_ACCESS_TOKEN_NAME] is unset. Twenty injects these two env vars only while dispatching a logic function or front component, so the guard detects calls from any other execution context and refuses to build a URL it cannot authenticate or scope.

Source

Thrown at packages/twenty-sdk/src/sdk/utils/get-public-asset-url.ts:25

  token: string,
): { workspaceId: string; applicationId: string } => {
  const payload = JSON.parse(atob(token.split('.')[1]));

  return {
    workspaceId: payload.workspaceId,
    applicationId: payload.applicationId,
  };
};

// Returns the public URL for a file in the app's public/ directory.
// Works in both logic functions and front components.
// The path is relative to the public/ folder (e.g. "images/logo.png").
export const getPublicAssetUrl = (path: string): string => {
  const apiUrl = process.env[DEFAULT_API_URL_NAME];
  const token = process.env[DEFAULT_APP_ACCESS_TOKEN_NAME];

  if (!apiUrl || !token) {
    throw new Error(
      'getPublicAssetUrl can only be called from within a logic function or front component',
    );
  }

  const { workspaceId, applicationId } = decodeTokenPayload(token);
  const withoutLeadingSlash = path.startsWith('/') ? path.slice(1) : path;
  const withPublicPrefix = withoutLeadingSlash.startsWith('public/')
    ? withoutLeadingSlash
    : `public/${withoutLeadingSlash}`;

  const encodedPath = withPublicPrefix
    .split('/')
    .map(encodeURIComponent)
    .join('/');

  return `${apiUrl}/public-assets/${workspaceId}/${applicationId}/${encodedPath}`;
};

View on GitHub (pinned to 1f5dd2bbd2)

Solutions

  1. Move the call inside a logic function or front component handler body so Twenty's runtime has already set the env vars.
  2. In tests, set process.env[DEFAULT_API_URL_NAME] and process.env[DEFAULT_APP_ACCESS_TOKEN_NAME] (import the constant names from twenty-shared/application) before invoking.
  3. Avoid invoking getPublicAssetUrl at module load time; defer it into the handler so it only runs in runtime context.

Example fix

// before (module scope, breaks)
export const logo = getPublicAssetUrl('images/logo.png');

// after (inside a logic/front handler)
export const handler = () => {
  const logo = getPublicAssetUrl('images/logo.png');
  return { logo };
};
Defensive patterns

Strategy: validation

Validate before calling

import { DEFAULT_API_URL_NAME, DEFAULT_APP_ACCESS_TOKEN_NAME } from 'twenty-shared/application';

const canCallGetPublicAssetUrl = (): boolean =>
  Boolean(process.env[DEFAULT_API_URL_NAME]) &&
  Boolean(process.env[DEFAULT_APP_ACCESS_TOKEN_NAME]);

// before calling:
if (!canCallGetPublicAssetUrl()) {
  // skip or seed env vars in tests
}

Prevention

When it happens

Trigger: Calling getPublicAssetUrl('images/logo.png') outside a Twenty-dispatched logic/front runtime: in a Jest test, a Node CLI script, a module top-level statement, or a worker that never received the app access token.

Common situations: Unit-testing SDK helpers without seeding the two env vars; importing SDK code into server bootstrap that runs before the runtime sets the token; SSR/build-time evaluation of a module that transitively reaches getPublicAssetUrl.

Related errors


AI-assisted analysis of twentyhq/twenty@1f5dd2bbd2 (2026-08-12). Data as JSON: /api/errors/cf8c7e57400be569. Report an issue: GitHub.