apache/beam · error

syntax error: unclosed

Error message

syntax error: unclosed '[' in pattern %q

What it means

The GCS filesystem's custom glob-to-regex converter rejected a pattern containing an unterminated character class: a '[' was opened but no matching ']' was found before the end of the pattern. This is a client-side syntax validation error thrown before any GCS API call.

Solutions

  1. Add the missing ']' to close the character class in the pattern.
  2. Escape literal '[' as '[[]' if a bracket is intended as a literal character.
  3. Log/print the final pattern before the List call to catch interpolation truncation.
  4. Validate the glob pattern with filesystem.Match or a regex dry-run before calling List.

Example fix

// before
glob := "gs://bucket/data[0-9" // unclosed class
files, err := fs.List(ctx, glob)
// after
glob := "gs://bucket/data[0-9]" // closed class
files, err := fs.List(ctx, glob)
Defensive patterns

Strategy: validation

Validate before calling

// validate the bracket classes are balanced before calling List
func validGlob(p string) bool { depth := 0; for _, r := range p { if r == '[' { depth++ }; if r == ']' { depth--; if depth < 0 { return false } } }; return depth == 0 }

Try / catch

if _, err := fs.List(ctx, glob); err != nil && strings.Contains(err.Error(), `unclosed '['`) {
	return fmt.Errorf("bad glob %q: %w", glob, err)
}

Prevention

When it happens

Trigger: Calling List (or Match through it) on a GCS filesystem with a glob like 'gs://bucket/data[0-9' where the '[' has no closing ']'.

Common situations: Dynamically built patterns with string interpolation truncating the class; user typos; patterns copied from shells where brackets were glob-expanded; escaping mistakes.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/3dd4d341bccd4e86. Report an issue: GitHub.

Appendix: source

Thrown at sdks/go/pkg/beam/io/filesystem/gcs/gcs.go:92

			} else {
				result.WriteString("[^/]*")
			}
		case '?':
			result.WriteString("[^/]")
		case '[':
			// Character class - find the closing bracket
			j := i + 1
			if j < len(runes) && runes[j] == '!' {
				j++
			}
			if j < len(runes) && runes[j] == ']' {
				j++
			}
			for j < len(runes) && runes[j] != ']' {
				j++
			}
			if j >= len(runes) {
				return nil, fmt.Errorf("syntax error: unclosed '[' in pattern %q", pattern)
			} else {
				// Copy the character class, converting ! to ^ for negation
				result.WriteByte('[')
				content := runes[i+1 : j]
				if len(content) > 0 && content[0] == '!' {
					result.WriteByte('^')
					content = content[1:]
				}
				result.WriteString(string(content))
				result.WriteByte(']')
				i = j
			}
		default:
			result.WriteString(regexp.QuoteMeta(string(c)))
		}
	}

	result.WriteString("$") // match end

View on GitHub (pinned to 12126d8942)