apache/beam · error
Invalid hook configuration for gcsRecorderHook
Error message
Invalid hook configuration for gcsRecorderHook: %s
What it means
The --performance_recording_hook=gcsRecorderHook option expects its argument to be a valid GCS object reference like bucket/path/prefix. gcsRecorderHook parses opts[0] with gcsx.ParseObject and panics if the string is not a parseable GCS object path.
Solutions
- Provide a well-formed GCS object path as the first hook option, e.g. my-bucket/path/to/prefix
- Verify the option format: gcsRecorderHook,<bucket>/<object-path>
- Log the opts value and test gcsx.ParseObject on it locally before submitting the job
- Remove the performance recording experiment if GCS recording is not needed
Example fix
// before --experiment=performance_recording_hook=gcsRecorderHook,myfolder // after --experiment=performance_recording_hook=gcsRecorderHook,my-bucket/perf/prefix
Defensive patterns
Strategy: validation
Validate before calling
opts := strings.SplitN(hookArg, ",", 2)
if len(opts) < 2 || !strings.Contains(opts[1], "/") {
return fmt.Errorf("gcsRecorderHook requires bucket/object path, got %q", hookArg)
}
if _, _, err := gcsx.ParseObject(opts[1]); err != nil {
return err
} Try / catch
func safeHook(opts []string) (perf.CaptureHook, err error) {
defer func() { if r := recover(); r != nil { err = fmt.Errorf("bad hook config: %v", r) } }()
return gcsRecorderHook(opts), nil
} Prevention
- Validate the hook argument format before launching the job
- Use full bucket-qualified paths: bucket/folder/prefix
- Test ParseObject on the configured string locally
- Keep hook configuration in version-controlled launch scripts
When it happens
Trigger: Passing a malformed value to the perf capture hook, e.g. --experiment=performance_recording_hook=gcsRecorderHook,nota,gcs,path or an option string without a bucket-qualified object path (missing bucket or empty string).
Common situations: Typos in the Dataflow job option, forgetting the gs://-style bucket prefix format expected by ParseObject, or passing extra comma-separated fields that corrupt opts[0].
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- could not resolve to a temp directory for import batch files
- BigQuery temp location expected a valid 'gs://' path, but…
- Could not upload to GCS path
- expected 1 option, got
- ib.options.cache_root needs to be a Cloud Storage Bucket to…
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/5a17b4a38e8eb779.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/go/pkg/beam/runners/dataflow/dataflow.go:439
WorkerZone: *workerZone,
TeardownPolicy: *teardownPolicy,
ContainerImage: getContainerImage(ctx),
Update: *update,
TransformNameMapping: updateTransformMapping,
}
if opts.TempLocation == "" {
opts.TempLocation = gcsx.Join(*stagingLocation, "tmp")
}
checkSoftDeletePolicyEnabled(ctx, opts.TempLocation, "temp_location")
return opts, nil
}
func gcsRecorderHook(opts []string) perf.CaptureHook {
bucket, prefix, err := gcsx.ParseObject(opts[0])
if err != nil {
panic(fmt.Sprintf("Invalid hook configuration for gcsRecorderHook: %s", opts))
}
return func(ctx context.Context, spec string, r io.Reader) error {
client, err := gcsx.NewClient(ctx, storage.ScopeReadWrite)
if err != nil {
return errors.WithContext(err, "establishing GCS client")
}
return gcsx.WriteObject(ctx, client, bucket, path.Join(prefix, spec), r)
}
}
func getContainerImage(ctx context.Context) string {
urn := jobopts.GetEnvironmentUrn(ctx)
if urn == "" || urn == "beam:env:docker:v1" {
if *workerHarnessImage != "" {
if *image != "" {
panic("Both worker_harness_container_image and sdk_container_image cannot both be set. Prefer sdk_container_image, worker_harness_container_image is deprecated.")
}View on GitHub (pinned to 12126d8942)