{"record":{"id":"4a0a9c38c45d011c","repo":"mikefarah/yq","slug":"cannot-join-with-v-can-only-join-arrays-of-scala","errorCode":null,"errorMessage":"cannot join with %v, can only join arrays of scalars","messagePattern":"cannot join with (.+?), can only join arrays of scalars","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/yqlib/operator_strings.go","lineNumber":509,"sourceCode":"\nfunc joinStringOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {\n\tlog.Debugf(\"joinStringOperator\")\n\tjoinStr := \"\"\n\n\trhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.RHS)\n\tif err != nil {\n\t\treturn Context{}, err\n\t}\n\tif rhs.MatchingNodes.Front() != nil {\n\t\tjoinStr = rhs.MatchingNodes.Front().Value.(*CandidateNode).Value\n\t}\n\n\tvar results = list.New()\n\n\tfor el := context.MatchingNodes.Front(); el != nil; el = el.Next() {\n\t\tnode := el.Value.(*CandidateNode)\n\t\tif node.Kind != SequenceNode {\n\t\t\treturn Context{}, fmt.Errorf(\"cannot join with %v, can only join arrays of scalars\", node.Tag)\n\t\t}\n\t\tresult := node.CreateReplacement(join(node.Content, joinStr))\n\t\tresults.PushBack(result)\n\t}\n\n\treturn context.ChildContext(results), nil\n}\n\nfunc join(content []*CandidateNode, joinStr string) (Kind, string, string) {\n\tvar stringsToJoin []string\n\tfor _, node := range content {\n\t\tstr := node.Value\n\t\tif node.Tag == \"!!null\" {\n\t\t\tstr = \"\"\n\t\t}\n\t\tstringsToJoin = append(stringsToJoin, str)\n\t}\n","sourceCodeStart":491,"sourceCodeEnd":527,"githubUrl":"https://github.com/mikefarah/yq/blob/8b5af0694bb82b41d4ae180fac9972029066f90a/pkg/yqlib/operator_strings.go#L491-L527","documentation":"The join operator ('join(sep)') in operator_strings.go only accepts sequence (array) nodes; it builds a single string from the array's scalar elements. If the node being joined has any other kind — a map, a scalar, or null — yq refuses with this message, reporting the node's tag.","triggerScenarios":"Calling 'join(\",\")' on a node that is not an array: e.g. '.tags | join(\",\")' when .tags is a single string, a map (object), or null; or piping a mapping like '{a: 1, b: 2} | join(\",\")'.","commonSituations":"Config files where a field is sometimes a single scalar and sometimes a list; users trying to join object keys/values without converting first (forgot 'keys' or 'to_entries | map(.value)'); empty/null fields produced by 'select' filters feeding into join.","solutions":["Ensure the input is an array: wrap scalars with '[.x]' or use 'collect': '[.tags] | join(\",\")'.","For maps, convert first: '.obj | to_entries | map(.value) | join(\",\")' or '.obj | keys | join(\",\")'.","Filter out nulls before joining: 'map(select(. != null)) | join(\",\")'."],"exampleFix":"// before (fails: .tags may be a scalar)\nyq '.tags | join(\",\")' config.yaml\n\n// after\nyq '[.tags] | join(\",\")' config.yaml","handlingStrategy":"validation","validationCode":"yq '.tags | type' file.yaml   # must be '!!seq' before join\n// script check:\nT=$(yq '.tags | type' file.yaml); [ \"$T\" = \"!!seq\" ] || { echo \"join needs an array, got $T\"; exit 1; }","typeGuard":"// in yq expression: select(type == \"!!seq\") | join(\",\")\nfunction isSequenceNode(node) { return node && node.kind === 'sequence'; }","tryCatchPattern":"if ! out=$(yq '.tags | join(\",\")' file.yaml 2>&1); then echo \"join failed: $out\"; out=\"$(yq '[.tags] | join(\",\")' file.yaml)\"; fi  # fallback to wrapping scalar in array","preventionTips":["Wrap possibly-scalar values in '[...]' or use 'collect' before join.","Convert maps with 'to_entries'/'keys' before joining.","Strip nulls with 'map(select(. != null))' before joining heterogeneous data."],"tags":["yq","join","type-mismatch","arrays"],"backgroundTag":"join-requires-array-input","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"}