{"record":{"id":"22b446a27544f560","repo":"mikefarah/yq","slug":"panic-err-on-copier-copy-failure-of-traverse-pref","errorCode":null,"errorMessage":"panic(err) on copier.Copy failure of traverse preferences","messagePattern":"panic\\(err\\) on copier\\.Copy failure of traverse preferences","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/yqlib/operators.go","lineNumber":196,"sourceCode":"\t}\n\tnoob := owner.CreateReplacement(ScalarNode, \"!!bool\", valString)\n\tif owner.IsMapKey {\n\t\tnoob.IsMapKey = false\n\t\tnoob.Key = owner\n\t}\n\n\treturn noob\n}\n\nfunc createTraversalTree(path []interface{}, traversePrefs traversePreferences, targetKey bool) *ExpressionNode {\n\tif len(path) == 0 {\n\t\treturn &ExpressionNode{Operation: &Operation{OperationType: selfReferenceOpType}}\n\t} else if len(path) == 1 {\n\t\tlastPrefs := traversePrefs\n\t\tif targetKey {\n\t\t\terr := copier.Copy(&lastPrefs, traversePrefs)\n\t\t\tif err != nil {\n\t\t\t\tpanic(err)\n\t\t\t}\n\t\t\tlastPrefs.IncludeMapKeys = true\n\t\t\tlastPrefs.DontIncludeMapValues = true\n\t\t}\n\t\treturn &ExpressionNode{Operation: &Operation{OperationType: traversePathOpType, Preferences: lastPrefs, Value: path[0], StringValue: fmt.Sprintf(\"%v\", path[0])}}\n\t}\n\n\treturn &ExpressionNode{\n\t\tOperation: &Operation{OperationType: shortPipeOpType},\n\t\tLHS:       createTraversalTree(path[0:1], traversePrefs, false),\n\t\tRHS:       createTraversalTree(path[1:], traversePrefs, targetKey),\n\t}\n}\n","sourceCodeStart":178,"sourceCodeEnd":210,"githubUrl":"https://github.com/mikefarah/yq/blob/8b5af0694bb82b41d4ae180fac9972029066f90a/pkg/yqlib/operators.go#L178-L210","documentation":"createTraversalTree builds an implicit traverse expression from a resolved path (used by assignment, comments, and append operators). When the path has a single element and targetKey is true, it copies traversePreferences with jinzhu/copier; any error from copier.Copy triggers panic(err) at operators.go:194-197. copier only fails on struct-copy incompatibilities, so in practice this is an internal invariant failure surfaced as a crash rather than a user-input error.","triggerScenarios":"Calling paths that reach createTraversalTree with targetKey=true and a single-element path: setPathOperator/applyAssignment via key-only assignment (e.g. `yq '.a.b key = ...'`/keys operations), applyPropertyComments/DeeplyAssign comment application on a map key, arrayAppend, or getPathToUse — i.e. essentially any operation that mutates map *keys* rather than values, if copier encounters incompatible fields.","commonSituations":"Custom builds where traversePreferences gained fields copier cannot copy (e.g. func or unexported/chan fields added by embedding or forks); version mismatches after upgrading yqlib with vendored forks; practically never from user YAML input itself.","solutions":["Verify you are running an unmodified, version-matched yq build; rebuild from a clean checkout (go build -o yq .) to rule out a patched traversePreferences struct.","Update to the latest yq release; if reproducible upstream, file a bug with the exact expression used (key-targeting assignment/comment expression).","If you fork yqlib, ensure any new fields in traversePreferences are copy-compatible with jinzhu/copier (exported, basic types) or implement a Copy() method copier will use.","As a workaround, avoid key-targeting forms (assign to values or use explicit map reconstruction) until fixed.","In library code, wrap calls in recover() if you must host yqlib inside a long-running process, since this is a panic not an error return."],"exampleFix":"// before (fork with new field)\ntype traversePreferences struct { ... ; OnKey func(*CandidateNode) bool }\n\n// after\ntype traversePreferences struct { ... } // keep fields copy-compatible, or add:\nfunc (p traversePreferences) Copy() traversePreferences { p2 := p; /* manual copy */; return p2 }","handlingStrategy":"try-catch","validationCode":"// Go host: confirm stock build (unmodified traversePreferences) before use\n// no user-side data validation can prevent this; verify binary integrity instead:\n// go build -o yq . && ./yq --version  # matches upstream release","typeGuard":"func prefsCopyCompatible(v reflect.Value) bool {\n    t := v.Type()\n    for i := 0; i < t.NumField(); i++ {\n        f := t.Field(i)\n        if !f.IsExported() || f.Type.Kind() == reflect.Func || f.Type.Kind() == reflect.Chan {\n            return false\n        }\n    }\n    return true\n}","tryCatchPattern":"func runKeyAssignment(expr string) (err error) {\n    defer func() { if r := recover(); r != nil { err = fmt.Errorf(\"createTraversalTree panic: %v\", r) } }()\n    // ... invoke yqlib setPathOperator/applyAssignment ...\n    return nil\n}","preventionTips":["Run stock yq builds; avoid forks that add func/unexported fields to traversePreferences","Keep yqlib and vendored copier versions in sync when upgrading","Always recover() around embedded yqlib calls in long-running processes","If a panic reproduces on a stock build with a key-targeting expression, report it upstream with the expression"],"tags":["panic","yq","internal","preferences-copy"],"backgroundTag":"reflection-copy-panic","analyzedSha":"8b5af0694bb82b41d4ae180fac9972029066f90a","analyzedAt":"2026-09-05T10:57:22.766Z","contentChangedAt":"2026-09-05T10:57:22.766Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}