Skip to content

StepFunctions parser: states:::states:startExecution.sync fails with KeyError 'Input' (PascalCase params not converted to boto casing) #10076

Description

@tinovyatkin

Summary

The arn:aws:states:::states:startExecution.sync / .sync:2 service integration (a parent state machine starting a child state machine) fails under the Step Functions parser backend with States.Runtime / cause 'Input'.

Root cause: the parser passes the ASL PascalCase parameter keys (Input, StateMachineArn) straight to boto3's start_execution, whose members are lowercase (stateMachineArn / name / input / traceHeader). botocore's serializer then raises KeyError: 'Input'.

This appears to be a regression introduced when the parser was ported from LocalStack (#9142): the base StateTaskService._normalise_parameters / _normalise_response — which in LocalStack convert the request/response between SFN PascalCase and the boto member casing — were reduced to pass in moto, while the helper methods they used to call (_to_boto_request / _from_boto_response) were kept but are now never invoked.

It goes unnoticed because the SFN→SNS / →DynamoDB / →SQS integration tests use services whose boto members are already PascalCase (TopicArn, Message, TableName), so verbatim passthrough happens to work. There is no SFN→SFN (startExecution.sync) integration test, and start_execution is the case where SFN's own API members are lowercase.

How to reproduce

import json, time
import boto3
from moto.server import ThreadedMotoServer
from moto.core.config import default_user_config

# Enable the ASL interpreter (parser backend)
default_user_config["stepfunctions"]["execute_state_machine"] = True

srv = ThreadedMotoServer(port=5000)
srv.start()
sfn = boto3.client("stepfunctions", endpoint_url="http://localhost:5000",
                   region_name="us-east-1", aws_access_key_id="x", aws_secret_access_key="x")
role = "arn:aws:iam::123456789012:role/sfn"

child = sfn.create_state_machine(
    name="child", roleArn=role,
    definition=json.dumps({"StartAt": "P", "States": {"P": {"Type": "Pass", "End": True}}}),
)["stateMachineArn"]

parent = sfn.create_state_machine(
    name="parent", roleArn=role,
    definition=json.dumps({
        "StartAt": "CallChild",
        "States": {
            "CallChild": {
                "Type": "Task",
                "Resource": "arn:aws:states:::states:startExecution.sync:2",
                "Parameters": {"Input": {"foo": "bar"}, "StateMachineArn": child},
                "End": True,
            }
        },
    }),
)["stateMachineArn"]

exe = sfn.start_execution(stateMachineArn=parent, name="r1", input="{}")["executionArn"]
for _ in range(40):
    d = sfn.describe_execution(executionArn=exe)
    if d["status"] != "RUNNING":
        break
    time.sleep(0.25)
print("STATUS:", d["status"])   # FAILED
print("ERROR:", d.get("error")) # States.Runtime
print("CAUSE:", d.get("cause")) # An error occurred while executing the state 'CallChild' ... 'Input'
srv.stop()

Expected

The parent execution reaches SUCCEEDED (the child runs and the sync:2 task returns its describe-execution output), as it does on real AWS Step Functions.

Actual

STATUS: FAILED
ERROR: States.Runtime
CAUSE: An error occurred while executing the state 'CallChild' (entered at the event id #4). 'Input'

The underlying exception (visible if you log ExecutionState._from_error) is:

File ".../botocore/serialize.py", line 473, in _serialize_type_structure
    member_shape = members[member_key]
                   ~~~~~~~^^^^^^^^^^^^
KeyError: 'Input'

from state_task_service_sfn.py::_eval_service_taskgetattr(sfn_client, "start_execution")(**normalised_parameters) with normalised_parameters = {"Input": "...", "StateMachineArn": "..."}.

Pointers

  • moto/stepfunctions/parser/asl/component/state/exec/state_task/service/state_task_service.py — base _normalise_parameters (and _normalise_response) are pass; the _to_boto_request / _from_boto_response helpers are present but never called.
  • Compare LocalStack's equivalent base _normalise_parameters, which looks up the boto input_shape and calls _to_boto_request(parameters, input_shape) to convert the casing before the boto call.
  • state_task_service_sfn.py::_normalise_parameters only JSON-stringifies Input; it relies on the (now-missing) base normalisation to fix key casing.

Environment

  • moto 5.1.22
  • boto3 1.42.93 / botocore 1.42.93
  • Python 3.12.13

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions