What were you trying to accomplish?
We run cfn-lint with serverless rules (-a cfn_lint_serverless.rules) in CI on SAM templates. Our API Gateway stages set access log format via a parameter: Format: !Ref ApiAccessLogFormat. The linter crashes with E0002 instead of either passing or reporting a normal WS2001 finding.
Expected Behavior
WS2001 (API Gateway Structured Logging) should either:
- Report a warning if the format is not valid structured JSON, or
- Skip / handle the case when
Format is an intrinsic (e.g. !Ref) without raising an exception.
No uncaught exception and no E0002.
Current Behavior
When a template has AccessLogSetting.Format (REST) or AccessLogSettings.Format (HTTP) set to !Ref SomeParameter, rule WS2001 raises an AttributeError and cfn-lint reports E0002 (“Unknown exception while processing rule WS2001”) with:
'dict_node' object has no attribute 'strip'
Root cause: In cfn-lint-serverless/cfn_lint_serverless/rules/api_gateway.py, ApiGatewayStructuredLoggingRule.match() reads Format from the template (lines 104 / 106). When the template has Format: !Ref ..., that value is a dict-like node (e.g. {"Ref": "ApiAccessLogFormat"}), not a string. It is then passed to _check_log_format(log_format), which at line 80 does log_format = log_format.strip() without checking the type. Calling .strip() on that node causes the crash.
Possible Solution
In _check_log_format, only call .strip() when the value is actually a string. For example, at the start of _check_log_format:
- If
log_format is not a string (e.g. it’s a dict/node, as with !Ref / !Sub), return True (skip validation) or treat it as “not structured logging” and return False without calling .strip(), so no exception is raised.
Example guard:
def _check_log_format(self, log_format: str) -> bool:
if not isinstance(log_format, str):
return True # skip validation when Format is intrinsic (Ref/Sub/etc.)
log_format = log_format.strip()
# ... rest unchanged
Alternatively, the rule could explicitly detect Ref/Sub nodes (e.g. via cfn-lint helpers) and skip or handle them before calling _check_log_format.
Steps to Reproduce (for bugs)
- Install:
pip install cfn-lint cfn-lint-serverless (cfn-lint 1.44.0).
- Save the following as
template.yaml:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Reproduce WS2001 crash when Format is !Ref
Parameters:
LogFormat:
Type: String
Default: '{"requestId":"$context.requestId"}'
Resources:
Api:
Type: AWS::Serverless::Api
Properties:
StageName: prod
AccessLogSetting:
DestinationArn: !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/apigateway'
Format: !Ref LogFormat
- Run:
cfn-lint template.yaml -a cfn_lint_serverless.rules
- Observe E0002 with message
'dict_node' object has no attribute 'strip'.
Environment
- Infrastructure as code technology used: AWS SAM (CloudFormation + Serverless transform), YAML templates.
- (for
cfn-lint) Python, cfn-lint, and cfn-lint-serverless versions: Python 3.x, cfn-lint 1.44.0, cfn-lint-serverless (from PyPI: 0.3.5).
- Debugging logs
$ cfn-lint template.yaml -a cfn_lint_serverless.rules
E0002 Unknown exception while processing rule WS2001: 'dict_node' object has no attribute 'strip'
template.yaml:19:7
What were you trying to accomplish?
We run
cfn-lintwith serverless rules (-a cfn_lint_serverless.rules) in CI on SAM templates. Our API Gateway stages set access log format via a parameter:Format: !Ref ApiAccessLogFormat. The linter crashes with E0002 instead of either passing or reporting a normal WS2001 finding.Expected Behavior
WS2001 (API Gateway Structured Logging) should either:
Formatis an intrinsic (e.g.!Ref) without raising an exception.No uncaught exception and no E0002.
Current Behavior
When a template has
AccessLogSetting.Format(REST) orAccessLogSettings.Format(HTTP) set to!Ref SomeParameter, rule WS2001 raises anAttributeErrorand cfn-lint reports E0002 (“Unknown exception while processing rule WS2001”) with:'dict_node' object has no attribute 'strip'Root cause: In cfn-lint-serverless/cfn_lint_serverless/rules/api_gateway.py,
ApiGatewayStructuredLoggingRule.match()readsFormatfrom the template (lines 104 / 106). When the template hasFormat: !Ref ..., that value is a dict-like node (e.g.{"Ref": "ApiAccessLogFormat"}), not a string. It is then passed to_check_log_format(log_format), which at line 80 doeslog_format = log_format.strip()without checking the type. Calling.strip()on that node causes the crash.Possible Solution
In
_check_log_format, only call.strip()when the value is actually a string. For example, at the start of_check_log_format:log_formatis not a string (e.g. it’s a dict/node, as with!Ref/!Sub), returnTrue(skip validation) or treat it as “not structured logging” and returnFalsewithout calling.strip(), so no exception is raised.Example guard:
Alternatively, the rule could explicitly detect Ref/Sub nodes (e.g. via cfn-lint helpers) and skip or handle them before calling
_check_log_format.Steps to Reproduce (for bugs)
pip install cfn-lint cfn-lint-serverless(cfn-lint 1.44.0).template.yaml:cfn-lint template.yaml -a cfn_lint_serverless.rules'dict_node' object has no attribute 'strip'.Environment
cfn-lint) Python, cfn-lint, and cfn-lint-serverless versions: Python 3.x, cfn-lint 1.44.0, cfn-lint-serverless (from PyPI: 0.3.5).