{"record":{"id":"7fc972e1ef63a04b","repo":"apache/beam","slug":"zero-length-key-v-v","errorCode":null,"errorMessage":"zero length key: %v %v","messagePattern":"zero length key: (.+?) (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/runners/prism/internal/engine/elementmanager.go","lineNumber":1548,"sourceCode":"\t\torigPending = append(origPending, e)\n\t}\n\tnewPending = origPending\n\n\tif ss.pendingByKeys == nil {\n\t\tss.pendingByKeys = map[string]*dataAndTimers{}\n\t}\n\n\ttype windowKey struct {\n\t\twindow typex.Window\n\t\tkey    string\n\t}\n\tpendingWindowKeys := set[windowKey]{}\n\n\tcount := 0\n\tfor _, e := range newPending {\n\t\tcount++\n\t\tif len(e.keyBytes) == 0 {\n\t\t\tpanic(fmt.Sprintf(\"zero length key: %v %v\", ss.ID, ss.inputID))\n\t\t}\n\t\tdnt, ok := ss.pendingByKeys[string(e.keyBytes)]\n\t\tif !ok {\n\t\t\tdnt = &dataAndTimers{}\n\t\t\tss.pendingByKeys[string(e.keyBytes)] = dnt\n\t\t}\n\t\theap.Push(&dnt.elements, e)\n\n\t\tif em.config.StreamingMode {\n\t\t\t// In streaming mode, we check trigger readiness on each element\n\t\t\tcount += ss.injectTriggeredBundlesIfReady(em, e.window, string(e.keyBytes))\n\t\t} else {\n\t\t\t// In batch mode, we store key + window pairs here and check trigger readiness for each of them later.\n\t\t\tpendingWindowKeys.insert(windowKey{window: e.window, key: string(e.keyBytes)})\n\t\t}\n\t}\n\tif !em.config.StreamingMode {\n\t\tfor wk := range pendingWindowKeys {","sourceCodeStart":1530,"sourceCodeEnd":1566,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/runners/prism/internal/engine/elementmanager.go#L1530-L1566","documentation":"When Prism adds newly pending elements to a keyed stage's per-key pending map, it requires every element to have a non-empty key byte string. A zero-length key means the element's key was never extracted or was encoded with an empty key coder, which would collapse all elements onto one bogus key. The runner panics with the stage and input IDs to localize the stage.","triggerScenarios":"Elements arriving at a keyed (GroupByKey/CoGBK) stage where e.keyBytes is empty because the key extractor produced no bytes — e.g. an empty-key coder or a preceding stage emitting elements without keys.","commonSituations":"Pipelines where a DoFn emits KV with an empty/nil key in Go (e.g. KV{\"\", v}), custom key coders producing zero bytes, or upstream transform wiring that skips key extraction.","solutions":["Inspect the stage (ss.ID) and its input PCollection to find the transform emitting zero-length keys","Fix the upstream DoFn to never emit empty string keys; filter out empty keys before the GBK stage","Check the key coder configuration for that PCollection; a wrong coder can decode to empty bytes","Validate keys in a pre-GBK ParDo and fail fast with a descriptive user error instead of a runner panic"],"exampleFix":"// before: user DoFn emits empty key\ne.Emit(KV{\"\", value})\n// after: guard against empty keys before grouping\nif key == \"\" {\n    return fmt.Errorf(\"cannot emit element with empty key for GBK input\")\n}\ne.Emit(KV{key, value})","handlingStrategy":"validation","validationCode":"if key == \"\" {\n    return fmt.Errorf(\"element key must be non-empty before GBK\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never emit empty or zero-value keys into keyed stages","Filter or default empty keys in a ParDo before GroupByKey","Validate key coders produce non-empty encodings"],"tags":["go","beam","prism","key","panic"],"backgroundTag":"empty-required-field","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}