{"record":{"id":"c2119a92c68c9bd4","repo":"apache/beam","slug":"import-failed-options-read-only","errorCode":null,"errorMessage":"import failed: options read-only","messagePattern":"import failed: options read-only","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/core/runtime/options.go","lineNumber":87,"sourceCode":"// harness. The extra layer is currently needed due to Dataflow\n// expectations about this representation. Subject to change.\ntype RawOptionsWrapper struct {\n\tOptions      RawOptions `json:\"beam:option:go_options:v1\"`\n\tRunner       string     `json:\"beam:option:runner:v1\"`\n\tAppName      string     `json:\"beam:option:app_name:v1\"`\n\tExperiments  []string   `json:\"beam:option:experiments:v1\"`\n\tRetainDocker bool       `json:\"beam:option:retain_docker_containers:v1\"`\n\tParallelism  int        `json:\"beam:option:parallelism:v1\"`\n}\n\n// Import imports the options from previously exported data and makes the\n// options read-only. It panics if import is called twice.\nfunc (o *Options) Import(opt RawOptions) {\n\to.mu.Lock()\n\tdefer o.mu.Unlock()\n\n\tif o.ro {\n\t\tpanic(\"import failed: options read-only\")\n\t}\n\to.ro = true\n\to.opt = copyMap(opt.Options)\n}\n\n// Get returns the value of the key. If the key has not been set, it returns \"\".\nfunc (o *Options) Get(key string) string {\n\to.mu.Lock()\n\tdefer o.mu.Unlock()\n\n\treturn o.opt[key]\n}\n\n// Set defines the value of the given key. If the key is already defined, it\n// panics.\nfunc (o *Options) Set(key, value string) {\n\to.mu.Lock()\n\tdefer o.mu.Unlock()","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/core/runtime/options.go#L69-L105","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Call Import exactly once per Options instance, before building the pipeline.","Create a new Options instance (NewOptions) for each import instead of reusing a sealed one.","Aggregate all RawOptions into one map and import a single merged RawOptions."],"exampleFix":"// before\nopts.Import(raw)\nopts.Import(moreRaw) // panics\n// after\nmerged := options.NewRawOptions()\nfor k, v := range moreRaw.Options { merged.Set(k, v) }\nopts.Import(merged)","handlingStrategy":"validation","validationCode":"if opt.IsReadOnly() { // or track sealing yourself\n    return errors.New(\"options already imported; create a new Options\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Call Import exactly once per Options instance","Merge all RawOptions before a single Import","Never share one package-level Options across pipelines or test cases"],"tags":["go","apache-beam","options","panic"],"backgroundTag":"invalid-state-transition","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}