Skip to content

feature request: Support of $context.requestOverride.status|querystring in apigateway #12621

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
1 task done
pieronatan opened this issue May 15, 2025 · 6 comments
Open
1 task done
Assignees
Labels
aws:apigateway Amazon API Gateway status: in progress Currently being worked on type: bug Bug report

Comments

@pieronatan
Copy link

Is there an existing issue for this?

  • I have searched the existing issues

Feature description

LocalStack does not currently support overriding:

  • responseOverride.status to set HTTP response status
  • requestOverride.querystring to inject query parameters into the integration request

Both work correctly in AWS API Gateway when using Velocity templates (VTL), but fail in LocalStack.


Examples

Override response status code (VTL):

#set($statusCode = $context.authorizer.statusCode)
#if($statusCode == 200)
    ##     Mapping the parameters from the request to the backend
    #foreach($param in $input.params().querystring.keySet())
        #set($context.requestOverride.querystring[$param]= $input.params().querystring.get($param))
        #if($foreach.hasNext) #end
    #end
    {
        "statusCode": 200,
        "message": "Valid scope found"
    }
#else
    #set($context.responseOverride.status = $statusCode)
    {
        "error": $context.authorizer.error
        "statusCode": $statusCode,
    }
#end

In AWS → returns HTTP 200.
In LocalStack → returns HTTP 200, or fails with:

2025-05-15T04:12:58.145  WARN --- [et.reactor-5] l.s.a.n.e.h.gateway_except : Non Gateway Exception raised: line 5, column 13: expected assignment in set directive, got: ($context.requestOverride.querystrin ... ...
 Traceback (most recent call last):
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 350, in require_next_element
     element = element_spec(self.filename, self._full_text, self.end)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 289, in __init__
     self.parse()
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 1077, in parse
     (var_name,) = self.identity_match(self.START)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 306, in identity_match
     raise NoMatch()
 airspeed.operators.NoMatch
 
 During handling of the above exception, another exception occurred:
 
 Traceback (most recent call last):
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/rolo/gateway/chain.py", line 166, in handle
     handler(self, self.context, response)
   File "/opt/code/localstack/localstack-core/localstack/services/apigateway/next_gen/execute_api/handlers/integration_request.py", line 119, in __call__
     body, request_override = self.render_request_template_mapping(
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/localstack-core/localstack/services/apigateway/next_gen/execute_api/handlers/integration_request.py", line 186, in render_request_template_mapping
     body, request_override = self._vtl_template.render_request(
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/localstack-core/localstack/services/apigateway/next_gen/execute_api/template_mapping.py", line 193, in render_request
     result = self.render_vtl(template=template.strip(), variables=variables_copy)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/localstack-core/localstack/utils/aws/templating.py", line 119, in render_vtl
     rendered_template = t.merge(namespace)
                         ^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 92, in merge
     self.merge_to(namespace, output, loader)
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 102, in merge_to
     self.ensure_compiled()
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 97, in ensure_compiled
     self.root_element = TemplateBody(self.filename, self.content)
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 289, in __init__
     self.parse()
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 1351, in parse
     self.block = self.next_element(Block)
                  ^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 333, in next_element
     element = element_spec(self.filename, self._full_text, self.end)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 289, in __init__
     self.parse()
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 1368, in parse
     self.next_element(
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 339, in next_element
     element = element_class(self.filename, self._full_text, self.end)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 289, in __init__
     self.parse()
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 1043, in parse
     self.block = self.require_next_element(Block, "block")
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 350, in require_next_element
     element = element_spec(self.filename, self._full_text, self.end)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 289, in __init__
     self.parse()
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 1368, in parse
     self.next_element(
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 339, in next_element
     element = element_class(self.filename, self._full_text, self.end)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 289, in __init__
     self.parse()
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 1312, in parse
     self.block = self.next_element(Block)
                  ^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 333, in next_element
     element = element_spec(self.filename, self._full_text, self.end)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 289, in __init__
     self.parse()
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 1368, in parse
     self.next_element(
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 339, in next_element
     element = element_class(self.filename, self._full_text, self.end)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 289, in __init__
     self.parse()
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 1291, in parse
     self.assignment = self.require_next_element(Assignment, "assignment")
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 352, in require_next_element
     raise self.syntax_error(expected)
 airspeed.operators.TemplateSyntaxError: line 5, column 13: expected assignment in set directive, got: ($context.requestOverride.querystrin ... ...

Environment

  • LocalStack version: latest
  • Running via: Docker / Compose / CLI
  • Infrastructure: Terraform / AWS CLI

🧑‍💻 Implementation

No response

Anything else?

No response

@pieronatan pieronatan added status: triage needed Requires evaluation by maintainers type: feature New feature, or improvement to an existing feature labels May 15, 2025
@localstack-bot
Copy link
Collaborator

Welcome to LocalStack! Thanks for reporting your first issue and our team will be working towards fixing the issue for you or reach out for more background information. We recommend joining our Slack Community for real-time help and drop a message to LocalStack Support if you are a licensed user! If you are willing to contribute towards fixing this issue, please have a look at our contributing guidelines.

@cloutierMat
Copy link
Contributor

Hi @pieronatan, thank you for your report of this bug in LocalStack.

I am trying to get a good understanding of your use case so I can get to the issuemore quickly. Can you tell me which integration you are using, as it sometimes impacts how ApiGw behaves with VTL template and overrides. Also, I assume you are using a lambda authorizer and using it's response in the authorizer context?

In AWS → returns HTTP 200.

#set($context.responseOverride.status = $statusCode) is not impacting the response, right? As this variable is only used in the response templates, not in the request templates? Did you experience something different?

requestOverride.querystring

I believe there may be a gap in our VTL implementation when attempting to use #set on a dict with dynamic assignment using [$var]. I will look into that one first as it seems to be the error you are experiencing given the following log

airspeed.operators.TemplateSyntaxError: line 5, column 13: expected assignment in set directive, got: ($context.requestOverride.querystrin ... ...

Please, let me know if I am missing anything.

@cloutierMat cloutierMat self-assigned this May 15, 2025
@cloutierMat cloutierMat added status: response required Waiting for a response from the reporter type: bug Bug report aws:apigateway Amazon API Gateway and removed status: triage needed Requires evaluation by maintainers type: feature New feature, or improvement to an existing feature labels May 15, 2025
@pieronatan
Copy link
Author

I'm using a Lambda authorizer that returns custom metadata via the context object, including a statusCode. In my mapping template, I dynamically override the response status based on this value using context.responseOverride.status.

Attempts to hardcode the status code (e.g., setting it directly to a fixed value) resulted in errors.

#set($statusCode = 201)
#if($statusCode == 200)
    ##     Mapping the parameters from the request to the backend
    #foreach($param in $input.params().querystring.keySet())
        #if($foreach.hasNext) #end
    #end
    {
        "statusCode": 200,
        "message": "Valid scope found"
    }
#else
    #set($context.responseOverride.status = $statusCode)
    {
        "error": $context.authorizer.error
        "statusCode": $statusCode,
    }
#end
Non Gateway Exception raised: Error in template '<string>' at position 311-360 in expression: ($context.responseOverride.status = $statusCode)
 
 KeyError: 'responseOverride'
 Traceback (most recent call last):
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 370, in evaluate
     return self.evaluate_raw(*args)
            ^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 1089, in evaluate_raw
     cur = cur[term]
           ~~~^^^^^^
 KeyError: 'responseOverride'
 
 The above exception was the direct cause of the following exception:
 
 Traceback (most recent call last):
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/rolo/gateway/chain.py", line 166, in handle
     handler(self, self.context, response)
   File "/opt/code/localstack/localstack-core/localstack/services/apigateway/next_gen/execute_api/handlers/integration_request.py", line 122, in __call__
     body, request_override = self.render_request_template_mapping(
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/localstack-core/localstack/services/apigateway/next_gen/execute_api/handlers/integration_request.py", line 194, in render_request_template_mapping
     body, request_override = self._vtl_template.render_request(
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/localstack-core/localstack/services/apigateway/next_gen/execute_api/template_mapping.py", line 270, in render_request
     result = self.render_vtl(template=template.strip(), variables=variables_copy)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/localstack-core/localstack/utils/aws/templating.py", line 119, in render_vtl
     rendered_template = t.merge(namespace)
                         ^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 92, in merge
     self.merge_to(namespace, output, loader)
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 103, in merge_to
     self.root_element.evaluate(fileobj, namespace, loader)
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 370, in evaluate
     return self.evaluate_raw(*args)
            ^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 1359, in evaluate_raw
     self.block.evaluate(stream, namespace, loader)
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 370, in evaluate
     return self.evaluate_raw(*args)
            ^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 1394, in evaluate_raw
     child.evaluate(stream, namespace, loader)
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 370, in evaluate
     return self.evaluate_raw(*args)
            ^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 1064, in evaluate_raw
     self.else_block.evaluate(stream, namespace, loader)
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 370, in evaluate
     return self.evaluate_raw(*args)
            ^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 1394, in evaluate_raw
     child.evaluate(stream, namespace, loader)
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 370, in evaluate
     return self.evaluate_raw(*args)
            ^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 1294, in evaluate_raw
     self.assignment.evaluate(stream, namespace, loader)
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 375, in evaluate
     six.reraise(
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/six.py", line 723, in reraise
     raise value.with_traceback(tb)
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 370, in evaluate
     return self.evaluate_raw(*args)
            ^^^^^^^^^^^^^^^^^^^^^^^^
   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/airspeed/operators.py", line 1089, in evaluate_raw
     cur = cur[term]
           ~~~^^^^^^
 airspeed.operators.TemplateExecutionError: Error in template '<string>' at position 311-360 in expression: ($context.responseOverride.status = $statusCode)
 
 KeyError: 'responseOverride'

@localstack-bot localstack-bot removed the status: response required Waiting for a response from the reporter label May 15, 2025
@cloutierMat cloutierMat added the status: backlog Triaged but not yet being worked on label May 15, 2025
@cloutierMat
Copy link
Contributor

Thank you for the extra input I had time to run some quick test as well. I will keep you updated as the fix becomes available.

@bentsku
Copy link
Contributor

bentsku commented May 19, 2025

Hello @pieronatan and thanks for your report!

One fix for your issue by @cloutierMat has been merged and is available in the latest image.

You can now correctly override your response status code in your request templates.

However, we are still working on a fix concerning the assignation via #set and brackets to the request/response Override (#set($context.requestOverride.querystring[$param]= "value") and this one might be a bit harder to get right, as its support is fully missing from the Python VTL engine library.

However I could validate that you can use a workaround that also works in AWS:

You can replace the following line:
#set($context.requestOverride.querystring[$param]= $input.params().querystring.get($param))

With the following that has the exact same effect:
$context.requestOverride.querystring.put($param, $input.params().querystring.get($param))

It also works on AWS, and will work in LocalStack. I hope this workaround can unblock you until we can properly fix the issue. (beware however that it returns the value of the put, so you might need to move this call out of the way of the return values)

I'll still leave the issue open until we get the right fix. Thank you!

@cloutierMat
Copy link
Contributor

cloutierMat commented May 29, 2025

Hello @pieronatan,

Thank you for your patience as we are resolving this issue. I submitted a fix in the airspeed package. The dependency will be updated in LocalStack early next week. I will update you again once it is available in latest

@cloutierMat cloutierMat added status: in progress Currently being worked on and removed status: backlog Triaged but not yet being worked on labels May 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
aws:apigateway Amazon API Gateway status: in progress Currently being worked on type: bug Bug report
Projects
None yet
Development

No branches or pull requests

4 participants