nektos/act · critical
unclosed string.
Error message
unclosed string.
What it means
This is a panic, not a returned error: rewriteSubExpression (pkg/runner/expression.go:431) panics with 'unclosed string.' when, after entering a single-quoted string inside a ${{ }} expression, it cannot find a closing quote matching the strPattern `(?:''|[^'])*'` anywhere in the remaining input. The panic crashes the act process instead of surfacing as a workflow failure.
Source
Thrown at pkg/runner/expression.go:431
return strings.ReplaceAll(strings.ReplaceAll(in, "{", "{{"), "}", "}}")
}
func rewriteSubExpression(ctx context.Context, in string, forceFormat bool) (string, error) {
if !strings.Contains(in, "${{") || !strings.Contains(in, "}}") {
return in, nil
}
strPattern := regexp.MustCompile("(?:''|[^'])*'")
pos := 0
exprStart := -1
strStart := -1
var results []string
formatOut := ""
for pos < len(in) {
if strStart > -1 {
matches := strPattern.FindStringIndex(in[pos:])
if matches == nil {
panic("unclosed string.")
}
strStart = -1
pos += matches[1]
} else if exprStart > -1 {
exprEnd := strings.Index(in[pos:], "}}")
strStart = strings.Index(in[pos:], "'")
if exprEnd > -1 && strStart > -1 {
if exprEnd < strStart {
strStart = -1
} else {
exprEnd = -1
}
}
if exprEnd > -1 {
formatOut += fmt.Sprintf("{%d}", len(results))View on GitHub (pinned to 4f41128141)
Solutions
- Fix the workflow expression so every opening ' inside ${{ }} has a closing quote
- Escape single quotes inside YAML single-quoted scalars by doubling them ('')
- Move literal text containing }} out of the expression or into a shell variable
- If you maintain act, replace the panic with a returned error in rewriteSubExpression
Example fix
# before:
run: echo ${{ github.event_name == 'push }} more
# after:
run: echo ${{ github.event_name == 'push' }} more Defensive patterns
Strategy: validation
Validate before calling
// Block the process boundary: recover from panics in expression rewriting
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("expression rewrite failed (likely unclosed quote): %v", r)
}
}()
out, err := rewriteSubExpression(ctx, in, forceFormat) Try / catch
Wrap the call site with defer/recover in Go (act itself panics rather than returning an error), convert to an error naming the offending step, then fix the quote in the workflow.
Prevention
- Ensure every ' inside ${{ }} has its closing ' — }} inside a quoted string does not end the expression
- Double single-quotes ('') inside single-quoted YAML scalars
- Lint with actionlint before running act; it flags unterminated strings without crashing
- Never leave a lone backslash-quote at the end of expression values
When it happens
Trigger: An expression value containing an opening single quote inside ${{ }} with no terminating quote before end of input, e.g. `run: echo ${{ env.NAME == 'unclosed }}` — the }} inside the prospective string is treated as string content, so the scanner runs to EOF and panics. Any step field (run, if, env values) passing through rewriteSubExpression triggers it.
Common situations: Typos dropping a closing quote in expressions; YAML single-quote escaping mistakes (a lone ' instead of '') that leave the expression parser mid-string; strings that contain }} but were meant to be outside the expression.
Related errors
- expressions are not allowed here
- %sFailed to parse: %s
- %sinsert is not allowed here
- ErrShortRef
- Missing parameters for %s expected >= %v got %v
AI-assisted analysis of nektos/act@4f41128141 (2026-08-15).
Data as JSON: /api/errors/c53d4cd3d41cf48b.
Report an issue: GitHub.