{"record":{"id":"69e57dec6539ffe7","repo":"apache/beam","slug":"couldn-t-decode-characteristic-for-variant-v-handler-v-v","errorCode":null,"errorMessage":"couldn't decode characteristic for variant %v handler %v: %v","messagePattern":"couldn't decode characteristic for variant (.+?) handler (.+?): (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/runners/prism/internal/config/config.go","lineNumber":142,"sourceCode":"func (v *Variant) GetCharacteristics(handler string) any {\n\tif v == nil {\n\t\treturn nil\n\t}\n\tmd, ok := v.parent.metadata[handler]\n\tif !ok {\n\t\treturn nil\n\t}\n\trt := md.ConfigCharacteristic()\n\n\t// Get a pointer to the concrete value.\n\trtv := reflect.New(rt)\n\n\t// look up the handler urn in the variant.\n\tyn := v.handlers[handler]\n\t//\n\tif err := yn.Decode(rtv.Interface()); err != nil {\n\t\t// We prevalidated the config, so this shouldn't happen.\n\t\tpanic(fmt.Sprintf(\"couldn't decode characteristic for variant %v handler %v: %v\", v.name, handler, err))\n\t}\n\n\t// Return the value pointed to by the pointer.\n\treturn rtv.Elem().Interface()\n}\n\n// HandlerRegistry stores known handlers and their associated metadata needed to parse\n// the YAML configuration.\ntype HandlerRegistry struct {\n\tvariations map[string]*rawVariant\n\tmetadata   map[string]HandlerMetadata\n\n\t// cached names\n\tvariantIDs, handerIDs []string\n}\n\n// NewHandlerRegistry creates an initialized HandlerRegistry.\nfunc NewHandlerRegistry() *HandlerRegistry {","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/runners/prism/internal/config/config.go#L124-L160","documentation":"When reading a characteristic value for a runner variant+handler, config.go decodes the YAML node into the characteristic's Go type via reflection. The decode is expected to succeed because the config was pre-validated, so failure triggers a panic indicating the validated config and the decode path have diverged (an internal invariant break).","triggerScenarios":"GetCharacteristics is called with a handler key present in the variant's handlers map, but yn.Decode fails — e.g. the yaml.Node's shape doesn't match the characteristic's Go type, or KnownFields(true)-style strictness rejects content the pre-validation passed.","commonSituations":"Hand-edited or programmatically generated config YAML whose node structure mismatches the ConfigCharacteristic type; running a custom variant config; a bug where validation and decode use different type mappings.","solutions":["Check the variant/handler YAML content against the expected characteristic schema","Regenerate or simplify the config file to the documented format","Verify the handler key exists with a compatible value type (string vs struct, etc.)","File a bug with the config file if pre-validation passes but decode fails"],"exampleFix":"// before: mismatched yaml value type\nvariant:\n  handler: 123\n// after\nvariant:\n  handler: \"valid-string-value\"","handlingStrategy":"validation","validationCode":"// validate config against the schema before load\nif err := config.Validate(yamlBytes); err != nil { return fmt.Errorf(\"invalid characteristic config: %w\", err) }","typeGuard":null,"tryCatchPattern":"// GetCharacteristics panics by design; guard the loader\ndefer func() { if r := recover(); r != nil { err = fmt.Errorf(\"characteristic decode failed: %v\", r) } }()","preventionTips":["Only load config files matching the documented schema","Run the config through the validator CLI/loader before use","Avoid hand-generating yaml.Node structures programmatically"],"tags":["go","beam","prism","config","yaml","panic"],"backgroundTag":"internal-invariant-violation","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"}