Skip to content

strip_string_quotes=True also strips quotes inside expressions, producing invalid HCL #310

Description

@livingstaccato

Describe the bug

SerializationOptions(strip_string_quotes=True) strips quotes from string literals inside expressions, not just from top-level string values. The resulting expression text is no longer valid HCL, and the original cannot be recovered.

Software:

  • OS: macOS 15 (arm64)
  • Python version: 3.11.16
  • python-hcl2 version: 8.1.2; also reproduced on 8.1.0. (The option does not
    exist in 7.x, and 8.0.0rc1 predates this form of SerializationOptions, so
    8.1.0 is the earliest affected release.)

Snippet of HCL2 code causing the unexpected behaviour:

a = upper("x")
b = var.x ? "yes" : "no"
c = [for s in var.list : upper(s) if s != ""]

Actual behavior

import hcl2
from hcl2.utils import SerializationOptions

hcl2.loads(src, serialization_options=SerializationOptions(strip_string_quotes=True))
# {'a': '${upper(x)}',
#  'b': '${var.x ? yes : no}',
#  'c': '${[for s in var.list : upper(s) if s != ]}'}

upper(x) now references an identifier x rather than the string "x"; var.x ? yes : no references two undefined identifiers; and s != is a syntax error.

Expected behavior

Quote stripping should apply to string values, leaving quoted literals inside expression text alone:

{'a': '${upper("x")}', 'b': '${var.x ? "yes" : "no"}', 'c': '${[for s in var.list : upper(s) if s != ""]}'}

which is what 7.2.1 produced.

Impact

docs/06_migrating_to_v8.md presents strip_string_quotes=True as the v7 compatibility path and warns only that it is one-way (not round-trippable). It does not say that expression contents are corrupted, so it reads as safe for read-only consumers — which it is not, for any configuration containing a function call, a conditional, or a comparison against a string.

This matters more given #289 proposes making strip_string_quotes=True the default.

Workaround

Leave the option off and strip the outer quotes yourself, only when a value both starts and ends with ". That preserves expression interiors, since expressions arrive as bare ${...} strings without surrounding quotes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingserializationLarkElement IR to Python dict (forward path)workaround-availableIssue can be solved by a workaround

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions