Skip to content

Invoke operations

Invoke other Lambda functions durably

The invoke operation calls another Lambda function and waits for its result. When you call context.invoke(), the SDK checkpoints the start of the operation and suspends the calling function. The durable functions backend invokes the target function. The calling function does not consume compute while waiting for the result. When the target function completes, the backend checkpoints the result and then starts a new invocation to resume execution.

You can invoke both durable functions and standard on-demand Lambda functions.

Note

context.invoke() is a durable functions operation that invokes a Lambda function on your behalf. It is distinct from the Lambda Invoke API, which invokes a function directly. context.invoke() suspends the calling function entirely and resumes it in a new invocation when the result is ready.

Invoke lifecycle

sequenceDiagram
    participant Inv1 as Invocation 1
    participant Backend as Durable Backend
    participant Target as Target Function
    participant Inv2 as Invocation 2

    note over Inv1, Inv2: Single Execution

    Inv1->>Backend: context.invoke() → checkpoint start
    Backend->>Target: Invoke target function
    Inv1->>Backend: Suspend (invocation terminates, no compute)

    Target->>Target: Execute
    Target->>Backend: Return result
    Backend->>Backend: Checkpoint result

    Backend->>Inv2: Start new invocation, resume from checkpoint
    Inv2->>Inv2: Continue execution with result
  • Function The durable Lambda function. This contains your code.
  • Target Function The Lambda function targetted by the durable invoke.
  • Execution The complete end-to-end lifecycle of a durable function, spanning potentially multiple invocations.
  • Invocation A single Lambda invocation within the execution. The invocation terminates at the durable invoke operation. The backend starts a new invocation when the target function completes, and replays to resume at the invoke point with the result.

Invoke walkthrough

import { DurableContext, durableExecution } from "aws-durable-execution-sdk-js";

interface OrderEvent {
  orderId: string;
  amount: number;
}

interface ValidationResult {
  valid: boolean;
  reason?: string;
}

interface PaymentResult {
  transactionId: string;
}

export const handler = durableExecution(
  async (event: OrderEvent, context: DurableContext) => {
    const validation = await context.invoke<OrderEvent, ValidationResult>(
      "validate-order",
      "validate-order-function:live",
      { orderId: event.orderId, amount: event.amount },
    );

    if (!validation.valid) {
      return { status: "rejected", reason: validation.reason };
    }

    const payment = await context.invoke<OrderEvent, PaymentResult>(
      "process-payment",
      "payment-processor-function:live",
      { orderId: event.orderId, amount: event.amount },
    );

    return { status: "completed", transactionId: payment.transactionId };
  },
);
from aws_durable_execution_sdk_python import DurableContext, durable_execution


@durable_execution
def handler(event: dict, context: DurableContext) -> dict:
    validation = context.invoke(
        "validate-order-function:live",
        {"order_id": event["order_id"], "amount": event["amount"]},
        name="validate-order",
    )

    if not validation["valid"]:
        return {"status": "rejected", "reason": validation.get("reason")}

    payment = context.invoke(
        "payment-processor-function:live",
        {"order_id": event["order_id"], "amount": event["amount"]},
        name="process-payment",
    )

    return {"status": "completed", "transaction_id": payment["transaction_id"]}
import software.amazon.lambda.durable.DurableContext;
import software.amazon.lambda.durable.DurableHandler;

public class ProcessOrder extends DurableHandler<OrderEvent, OrderResult> {

    @Override
    public OrderResult handle(OrderEvent event, DurableContext context) {
        ValidationResult validation = context.invoke(
            "validate-order",
            "validate-order-function:live",
            event,
            ValidationResult.class
        );

        if (!validation.isValid()) {
            return OrderResult.rejected(validation.getReason());
        }

        PaymentResult payment = context.invoke(
            "process-payment",
            "payment-processor-function:live",
            event,
            PaymentResult.class
        );

        return OrderResult.completed(payment.getTransactionId());
    }
}

When this function runs:

  1. The SDK checkpoints the first invoke operation's start and triggers validate-order-function
  2. The calling function suspends until the validation result is available
  3. The backend checkpoints the invoke's result and reinvokes the calling function
  4. Execution resumes with the validation result
  5. The SDK checkpoints the second invoke's start and triggers payment-processor-function
  6. The calling function suspends again until the payment result is available

Method signature

invoke

// Named overload
invoke<TInput, TOutput>(
  name: string,
  funcId: string,
  input?: TInput,
  config?: InvokeConfig<TInput, TOutput>,
): DurablePromise<TOutput>

// Unnamed overload
invoke<TInput, TOutput>(
  funcId: string,
  input?: TInput,
  config?: InvokeConfig<TInput, TOutput>,
): DurablePromise<TOutput>

Parameters:

  • name (optional) A name for the invoke operation. Pass undefined to omit.
  • funcId (required) The function ID or ARN. An alias or version qualifier is required for durable functions. On-demand functions do not require a qualifier.
  • input (optional) The payload to send to the invoked function.
  • config (optional) An InvokeConfig object.

Returns: DurablePromise<TOutput>. Use await to get the result.

Throws: InvokeError if the invoked function fails or times out.

def invoke(
    function_name: str,
    payload: P,
    name: str | None = None,
    config: InvokeConfig[P, R] | None = None,
) -> R: ...

Parameters:

  • function_name (required) The function name or ARN. An alias or version qualifier is required for durable functions. On-demand functions do not require a qualifier.
  • payload (required) The payload to send to the invoked function.
  • name (optional) A name for the invoke operation.
  • config (optional) An InvokeConfig object.

Returns: R, the return value of the invoked function.

Raises: CallableRuntimeError if the invoked function fails or times out.

// sync
<T, U> T invoke(String name, String functionName, U payload, Class<T> resultType)
<T, U> T invoke(String name, String functionName, U payload, Class<T> resultType, InvokeConfig config)
<T, U> T invoke(String name, String functionName, U payload, TypeToken<T> resultType)
<T, U> T invoke(String name, String functionName, U payload, TypeToken<T> resultType, InvokeConfig config)

// async
<T, U> DurableFuture<T> invokeAsync(String name, String functionName, U payload, Class<T> resultType)
<T, U> DurableFuture<T> invokeAsync(String name, String functionName, U payload, Class<T> resultType, InvokeConfig config)
<T, U> DurableFuture<T> invokeAsync(String name, String functionName, U payload, TypeToken<T> resultType)
<T, U> DurableFuture<T> invokeAsync(String name, String functionName, U payload, TypeToken<T> resultType, InvokeConfig config)

Parameters:

  • name (required) A nullable name for the invoke operation.
  • functionName (required) The function name or ARN. An alias or version qualifier is required for durable functions. On-demand functions do not require a qualifier.
  • payload (required) The payload to send to the invoked function.
  • resultType (required) The Class<T> or TypeToken<T> for deserialization.
  • config (optional) An InvokeConfig object.

Returns: T (sync) or DurableFuture<T> (async via invokeAsync()).

Throws:

  • InvokeFailedException if the invoked function fails.
  • InvokeTimedOutException if the invocation times out (Python only, via
  • InvokeConfig.timeout). InvokeStoppedException if the invocation was stopped.

InvokeConfig

interface InvokeConfig<I, O> {
  payloadSerdes?: Serdes<I>;
  resultSerdes?: Serdes<O>;
  tenantId?: string;
}

Parameters:

  • payloadSerdes (optional) Custom Serdes<I> for the payload sent to the invoked function. Defaults to JSON serialization.
  • resultSerdes (optional) Custom Serdes<O> for the result returned from the invoked function. Defaults to JSON serialization.
  • tenantId (optional) Tenant identifier for multi-tenant isolation.
@dataclass(frozen=True)
class InvokeConfig(Generic[P, R]):
    serdes_payload: SerDes[P] | None = None
    serdes_result: SerDes[R] | None = None
    tenant_id: str | None = None

Parameters:

  • serdes_payload (optional) Custom SerDes for the payload. Defaults to JSON serialization.
  • serdes_result (optional) Custom SerDes for the result. Defaults to JSON serialization.
  • tenant_id (optional) Tenant identifier for multi-tenant isolation.
InvokeConfig.builder()
    .payloadSerDes(SerDes)  // optional
    .serDes(SerDes)         // optional, sets result SerDes
    .tenantId(String)       // optional
    .build()

Parameters:

  • payloadSerDes (optional) Custom SerDes for the payload. Defaults to JSON serialization.
  • serDes (optional) Custom SerDes for the result. Defaults to JSON serialization.
  • tenantId (optional) Tenant identifier for multi-tenant isolation.

Naming invoke operations

Name invoke operations to make them easier to identify in logs and tests. You can use names to describe what the invocation does rather than which function it calls.

The name is the first argument. Pass undefined to omit it.

Pass name as a keyword argument. If omitted, the operation has no name in logs.

The name is always the first argument. Pass null to omit it.

Configuration

Configure invoke behavior using InvokeConfig:

import {
  DurableContext,
  InvokeConfig,
  durableExecution,
} from "aws-durable-execution-sdk-js";

export const handler = durableExecution(
  async (event: { orderId: string }, context: DurableContext) => {
    const config: InvokeConfig<{ orderId: string }, { status: string }> = {
      tenantId: event.orderId,
    };

    const result = await context.invoke(
      "process-order",
      "order-processor-function:live",
      { orderId: event.orderId },
      config,
    );

    return result;
  },
);
from aws_durable_execution_sdk_python import DurableContext, durable_execution
from aws_durable_execution_sdk_python.config import InvokeConfig


@durable_execution
def handler(event: dict, context: DurableContext) -> dict:
    config = InvokeConfig(tenant_id=event.get("tenant_id"))

    result = context.invoke(
        "order-processor-function:live",
        {"order_id": event["order_id"]},
        name="process-order",
        config=config,
    )

    return result
import software.amazon.lambda.durable.DurableContext;
import software.amazon.lambda.durable.DurableHandler;
import software.amazon.lambda.durable.config.InvokeConfig;

public class InvokeWithConfig extends DurableHandler<OrderEvent, OrderResult> {

    @Override
    public OrderResult handle(OrderEvent event, DurableContext context) {
        InvokeConfig config = InvokeConfig.builder()
            .tenantId(event.getTenantId())
            .build();

        return context.invoke(
            "process-order",
            "order-processor-function:live",
            event,
            OrderResult.class,
            config
        );
    }
}

Error handling

Errors from the invoked function propagate to the calling function. Catch them to handle failures without letting them terminate the calling function.

import {
  DurableContext,
  InvokeError,
  durableExecution,
} from "aws-durable-execution-sdk-js";

export const handler = durableExecution(
  async (event: { orderId: string }, context: DurableContext) => {
    try {
      const result = await context.invoke(
        "process-payment",
        "payment-processor-function:live",
        { orderId: event.orderId },
      );
      return { status: "success", result };
    } catch (err) {
      if (err instanceof InvokeError) {
        return { status: "failed", reason: err.message };
      }
      throw err;
    }
  },
);
from aws_durable_execution_sdk_python import DurableContext, durable_execution
from aws_durable_execution_sdk_python.exceptions import CallableRuntimeError


@durable_execution
def handler(event: dict, context: DurableContext) -> dict:
    try:
        result = context.invoke(
            "payment-processor-function:live",
            {"order_id": event["order_id"]},
            name="process-payment",
        )
        return {"status": "success", "result": result}
    except CallableRuntimeError as e:
        return {"status": "failed", "reason": str(e)}
import software.amazon.lambda.durable.DurableContext;
import software.amazon.lambda.durable.DurableHandler;
import software.amazon.lambda.durable.exception.InvokeFailedException;
import software.amazon.lambda.durable.exception.InvokeTimedOutException;

public class HandleInvocationError extends DurableHandler<OrderEvent, OrderResult> {

    @Override
    public OrderResult handle(OrderEvent event, DurableContext context) {
        try {
            PaymentResult payment = context.invoke(
                "process-payment",
                "payment-processor-function:live",
                event,
                PaymentResult.class
            );
            return OrderResult.success(payment.getTransactionId());
        } catch (InvokeTimedOutException e) {
            return OrderResult.failed("payment timed out");
        } catch (InvokeFailedException e) {
            return OrderResult.failed(e.getMessage());
        }
    }
}

Java exposes separate exception types for different failure modes: InvokeFailedException for function errors, InvokeTimedOutException for timeouts (Python only), and InvokeStoppedException when the invocation was stopped. All extend InvokeException.

See also