Skip to content

[Bug]: Local Lambda HTTP service rejects DurableExecutionArn / CallbackId containing "/" #9039

Description

@yaythomas

Description

samcli/local/lambda_service/local_lambda_http_service.py registers Flask routes with the default <string> URL converter:

"/2025-12-01/durable-executions/<durable_execution_arn>"
"/2025-12-01/durable-executions/<durable_execution_arn>/history"
"/2025-12-01/durable-executions/<durable_execution_arn>/stop"
"/2025-12-01/durable-execution-callbacks/<callback_id>/succeed"
"/2025-12-01/durable-execution-callbacks/<callback_id>/fail"
"/2025-12-01/durable-execution-callbacks/<callback_id>/heartbeat"

The <string> converter does not match /. After aws-durable-execution-sdk-python-testing#216, DurableExecutionArn values are minted as <uuid>/<invocation-id> and boto percent-encodes the / as %2F in the URI label. Werkzeug decodes %2F back to / during routing, so the path no longer matches a single <string> segment and the request 404s before reaching the handler:

botocore.exceptions.ClientError: An error occurred (PathNotFoundLocally) when calling
the GetDurableExecutionHistory operation: PathNotFoundException

The same shape applies to base64-encoded CallbackId values, which contain /.

Failing tests

local-start-lambda job in Integration Tests #8779:

  • tests/integration/local/start_lambda/test_start_lambda_durable.py::TestStartLambdaDurable::test_local_callback_cli_fail_{0,1,2}
  • …test_local_callback_cli_heartbeat
  • …test_local_start_lambda_invoke_durable_function_{HelloWorld,MapOperations,NamedStep,NamedWait,Parallel}
  • …test_local_start_lambda_invoke_timeout
  • …test_local_start_lambda_invoke_wait_for_callback_{success,failure}_{cli,http}*

All fail with PathNotFoundLocally on GetDurableExecutionHistory, GetDurableExecution, or StopDurableExecution. Reproduced locally on develop.

Fix

Use Werkzeug <path:...> converters for the ARN/callback URI labels. <path> accepts / and Werkzeug picks the right rule via trailing-literal specificity (verified with a Flask test client: …/<path:arn>/history wins over …/<path:arn> for …/abc%2Fdef/history). Existing unquote() calls in handlers can stay — idempotent on already-decoded values. Backwards-compatible with old UUID-only paths.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions