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

  1. Provide a well-formed GCS object path as the first hook option, e.g. my-bucket/path/to/prefix
  2. Verify the option format: gcsRecorderHook,<bucket>/<object-path>
  3. Log the opts value and test gcsx.ParseObject on it locally before submitting the job
  4. 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

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


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)