Skip to content

lambda timeout ai agent: Resumable Task Design

Lambda timeout AI agent workflow using resumable tasks to continue long-running work beyond the AWS Lambda execution limit.

Increasing the timeout is not enough when your AI agent may run longer than Lambda allows. Model latency varies. Tool calls stall. Input sizes grow. A reliable design splits the job into bounded steps, persists progress, and resumes from the last committed checkpoint. This guide shows how to build that execution model without repeating completed work or holding an HTTP request open.

Know when a Lambda timeout requires a different architecture

AWS Lambda has a configurable execution timeout. The default is 3 seconds. You can set it from 1 to 900 seconds, in one-second increments. That makes 15 minutes the hard maximum for one invocation (AWS documentation).

You can change the configured timeout with the AWS CLI:

aws lambda update-function-configuration \
  --function-name my-function \
  --timeout 120

In AWS SAM, use the Timeout property:

Resources:
  AgentWorker:
    Type: AWS::Serverless::Function
    Properties:
      Handler: app.handler
      Runtime: python3.12
      Timeout: 120

A higher setting helps when the function already finishes within the Lambda limit and only needs more headroom. It does not solve workloads whose total duration is inherently unbounded.

A competitor-monitoring agent illustrates the problem. It may fetch documents, extract content, call a model, validate the answer, store a report, and send a notification. Each operation can complete successfully while the complete run still exceeds the invocation limit.

First identify which timeout actually failed:

  • Lambda timeout: The function reaches its configured execution limit.
  • Model timeout: The model client stops waiting for a response.
  • Downstream-service timeout: An HTTP client, database, or tool call exceeds its own deadline.
  • Agent integration timeout: The failure occurs before or around the handoff between an agent platform and Lambda.

Do not assume every error mentioning an agent or model came from Lambda. Correlate invocation logs with model and integration logs. Confirm whether the handler started and which operation was active.

Lambda remains useful here. The architectural change is to treat each invocation as a worker for part of the job, not as the entire job. See the broader serverless AI agent deployment model for the surrounding runtime tradeoffs.

AWS Lambda timeout limits and exampleAWS Lambda timeout limits and exampleDefault timeout3secondsExample configuration120secondsMaximum timeout900seconds

Design the agent as resumable, bounded steps

Turn one long run_agent() function into a state machine. Each state should have a narrow input, a durable output, and a clear completion condition.

For a periodic research agent, the workflow might be:

PLAN
  -> FETCH_SOURCE
  -> EXTRACT_CONTENT
  -> ANALYZE_ITEM
  -> VALIDATE_RESULTS
  -> BUILD_REPORT
  -> PUBLISH
  -> COMPLETE

Avoid defining ANALYZE_ALL_ITEMS as one step. Use a cursor so each invocation processes only the work that fits inside its budget.

A step should:

  1. Load the latest committed state.
  2. Perform a bounded unit of work.
  3. Persist its output and updated cursor.
  4. Schedule the continuation.
  5. Exit successfully.

Set a step deadline below the Lambda timeout. Keep a safety margin for writing the checkpoint, publishing the continuation, and emitting final logs. The worker must stop accepting new work when it enters that margin.

Use separate deadlines for external calls:

result = model_client.analyze(
    document,
    timeout=state["model_deadline_seconds"],
)

The model deadline should expire before the step deadline. The step deadline should expire before Lambda terminates the invocation. This gives your code time to record what happened.

AWS warns that runtime can vary with transferred data volume, processing complexity, and service latency. A timeout close to the average execution duration therefore leaves little protection against normal variation (AWS documentation).

Do not size a step around average behavior. Size it so the worker can checkpoint safely when inputs or dependencies are slower than expected.

Persist progress, outputs, and idempotency keys

Memory and temporary files are not your source of truth. Store task state in durable storage after every completed unit of work.

A practical state document looks like this:

{
  "task_id": "report-2026-07-29",
  "status": "RUNNING",
  "current_step": "ANALYZE_ITEM",
  "cursor": "source-key",
  "intermediate_outputs": {
    "plan_ref": "storage://tasks/report/plan.json",
    "analysis_ref": "storage://tasks/report/analysis.json"
  },
  "attempts": {
    "ANALYZE_ITEM": 1
  },
  "step_deadline": "configured-at-runtime",
  "last_error": null,
  "completed_effects": {
    "publish_report": false
  },
  "version": "current-state-version"
}

Store large artifacts separately and keep references in the state record. Examples include fetched pages, normalized documents, model responses, and report drafts. This keeps the control record small while preserving the evidence needed for recovery.

Tool calls also need durable records. If the agent already enriched a lead or published a report, a retry must not repeat that side effect.

Derive an idempotency key from the task and logical operation:

import hashlib

def idempotency_key(task_id: str, operation: str, item_id: str) -> str:
    raw = f"{task_id}:{operation}:{item_id}"
    return hashlib.sha256(raw.encode()).hexdigest()

Write the result against that key. Before executing the operation, check whether a completed result already exists. When the destination API supports idempotency keys, send the same key there as well.

The checkpoint and continuation should behave as one transition. Persist the new cursor before another worker can process it. Use conditional updates or version checks to prevent concurrent workers from committing conflicting state.

This control state is one part of an AI job deployment’s production anatomy: code, runtime configuration, secrets, scheduling, storage, and execution history must work together.

Choose the right continuation mechanism: Step Functions, SQS, or EventBridge

The continuation mechanism depends on how explicit your workflow must be.

Mechanism Good fit Tradeoffs
Step Functions Known workflow states, branches, retry policies, and approval points Strong workflow visibility, but more orchestration configuration and state-transition cost
SQS Durable units of work processed by decoupled workers Simple scaling and failure isolation, but ordering and workflow visibility require deliberate design
EventBridge Event-driven continuation, lifecycle notifications, and loose integration Clear producer-consumer separation, but task state and retry semantics remain application concerns

Use Step Functions when operators need to inspect a defined path such as planning, collection, analysis, review, and publication.

Use SQS when a cursor can produce independent work items. A document-processing agent can enqueue each document while keeping the report task in persistent state.

Use EventBridge when state changes should trigger other components. A ReportCompleted event might notify a webhook handler or start a separate publication job.

Human intervention also affects the choice. Explicit workflow states make approval and manual resume easier. Queue-based designs need an administrative path that can inspect state, correct it, and enqueue a safe continuation.

Run the workflow asynchronously. Return a task_id after accepting the request, then provide either:

  • A task-status endpoint.
  • A completion callback.
  • A completion event consumed by another service.

Do not keep the original HTTP connection open while the agent waits on models and tools.

Implement explicit recovery after timeout or failure

A resumable worker needs a deliberate control loop. The following Python-style pseudocode shows the core pattern:

import time

class RetryableError(Exception):
    pass

class PermanentError(Exception):
    pass

def handle(message, store, queue, agent, step_budget, safety_margin):
    task_id = message["task_id"]
    worker_id = message["worker_id"]

    state = store.claim(task_id, worker_id)
    if state is None or state["status"] in {"COMPLETE", "FAILED"}:
        return

    deadline = time.monotonic() + step_budget

    try:
        while state["status"] == "RUNNING":
            remaining = deadline - time.monotonic()

            if remaining <= safety_margin:
                store.checkpoint(state, expected_version=state["version"])
                queue.enqueue({"task_id": task_id})
                return

            state = agent.run_next_unit(
                state=state,
                operation_timeout=remaining - safety_margin,
            )

            state = store.checkpoint(
                state,
                expected_version=state["version"],
            )

        store.release(task_id, worker_id)

    except RetryableError as error:
        state["last_error"] = repr(error)
        state["attempts"][state["current_step"]] += 1
        store.checkpoint(state, expected_version=state["version"])
        queue.enqueue_with_backoff({"task_id": task_id})

    except PermanentError as error:
        store.mark_failed(
            task_id=task_id,
            step=state["current_step"],
            error=repr(error),
        )

The next worker loads current_step and cursor from storage. It does not reconstruct progress from logs. It resumes from the last committed checkpoint.

Classify failures explicitly:

  • Transient: Retry with backoff. Examples include temporary service unavailability.
  • Permanent: Mark the task failed and retain diagnostic context.
  • Budget exhausted: Checkpoint and enqueue a continuation without treating it as an error.
  • Abandoned: Detect a task whose claim expired, then make it eligible for recovery.
  • Repeated failure: Move it to a dead-letter or manual-recovery path.

A killed invocation may not execute cleanup code. Recovery must therefore rely on durable claims with expiration, not an in-memory lock.

AWS lists large or slow S3 downloads, slow service responses, and increased computation from complex inputs as common timeout causes (AWS documentation). Capture which dependency and input triggered the failure. “Task timed out” is not enough to decide whether to retry, split the unit further, or reject the input.

Test and observe multi-step AI jobs in production

Every log and trace should include task_id, step_id, and the attempt identifier. Emit structured events for:

  • Step started, checkpointed, continued, completed, or failed.
  • Current cursor and state transition.
  • Step duration and remaining budget.
  • Attempt count and error category.
  • Model token usage.
  • Tool and downstream-service latency.
  • Idempotency-key reuse.
  • Queue or orchestration handoff.

For implementation patterns, see this Python guide to AI agent observability.

Test interruption as a normal event. Terminate a worker before checkpointing, after checkpointing, and during an external side effect. Verify that the next invocation either resumes the unfinished unit or recognizes the committed result. It must not silently skip work or duplicate the side effect.

Use production-sized documents, realistic tool responses, and high-latency dependencies. AWS recommends testing with data volumes and parameters representative of expected upper production bounds rather than convenient small samples (AWS documentation).

Before release, verify:

  • [ ] Every step has a configured time budget.
  • [ ] Every external call has its own deadline.
  • [ ] The worker preserves time for checkpointing.
  • [ ] Progress survives a terminated invocation.
  • [ ] Each side effect has an idempotency key.
  • [ ] State updates reject conflicting writers.
  • [ ] Retryable and permanent errors follow different paths.
  • [ ] Abandoned claims can be recovered.
  • [ ] Repeated failures reach a manual-recovery path.
  • [ ] Logs correlate the full run by task and step.
  • [ ] Tests cover realistic data volume and dependency latency.

Conclusion

A Lambda timeout is not just a configuration problem. For long AI agent jobs, it is a workflow boundary. Break the agent into bounded units, persist each committed result, and make continuation explicit. That design also improves retries, debugging, and operator control.

A managed agent runtime such as HollowHost can reduce the deployment work around scheduling, stateful jobs, secrets, and observability. The application-level rule remains the same: no invocation should be the only place where your agent’s progress exists.