apache/beam · error

import failed: options read-only

Error message

import failed: options read-only

What it means

Options.Import seals the options object: it copies RawOptions in and flips the read-only flag so pipeline options can no longer change. Importing twice (or into an already-sealed Options) would allow mutating a pipeline after construction, so it panics.

Source

Thrown at sdks/go/pkg/beam/core/runtime/options.go:87

// harness. The extra layer is currently needed due to Dataflow
// expectations about this representation. Subject to change.
type RawOptionsWrapper struct {
	Options      RawOptions `json:"beam:option:go_options:v1"`
	Runner       string     `json:"beam:option:runner:v1"`
	AppName      string     `json:"beam:option:app_name:v1"`
	Experiments  []string   `json:"beam:option:experiments:v1"`
	RetainDocker bool       `json:"beam:option:retain_docker_containers:v1"`
	Parallelism  int        `json:"beam:option:parallelism:v1"`
}

// Import imports the options from previously exported data and makes the
// options read-only. It panics if import is called twice.
func (o *Options) Import(opt RawOptions) {
	o.mu.Lock()
	defer o.mu.Unlock()

	if o.ro {
		panic("import failed: options read-only")
	}
	o.ro = true
	o.opt = copyMap(opt.Options)
}

// Get returns the value of the key. If the key has not been set, it returns "".
func (o *Options) Get(key string) string {
	o.mu.Lock()
	defer o.mu.Unlock()

	return o.opt[key]
}

// Set defines the value of the given key. If the key is already defined, it
// panics.
func (o *Options) Set(key, value string) {
	o.mu.Lock()
	defer o.mu.Unlock()

View on GitHub (pinned to 12126d8942)

Solutions

  1. Call Import exactly once per Options instance, before building the pipeline.
  2. Create a new Options instance (NewOptions) for each import instead of reusing a sealed one.
  3. Aggregate all RawOptions into one map and import a single merged RawOptions.

Example fix

// before
opts.Import(raw)
opts.Import(moreRaw) // panics
// after
merged := options.NewRawOptions()
for k, v := range moreRaw.Options { merged.Set(k, v) }
opts.Import(merged)
Defensive patterns

Strategy: validation

Validate before calling

if opt.IsReadOnly() { // or track sealing yourself
    return errors.New("options already imported; create a new Options")
}

Prevention

When it happens

Trigger: Calling opt.Import(raw) a second time on the same *Options, or calling Import on an Options instance that was already sealed by a previous Import (the flag o.ro is true).

Common situations: Tests that import options once in a shared setup helper and again per-case; a library re-importing user options into a shared package-level Options; retry/re-execution logic that re-runs the option-import path.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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