{"record":{"id":"0efa56c8c535eb9f","repo":"go-task/task","slug":"loop-var-must-be-a-delimiter-separated-string-lis","errorCode":null,"errorMessage":"loop var must be a delimiter-separated string, list or a map","messagePattern":"loop var must be a delimiter-separated string, list or a map","errorType":"validation","errorClass":"TaskfileInvalidError","httpStatus":null,"severity":"error","filePath":"variables.go","lineNumber":458,"sourceCode":"\t\t\t\t\t\tvalues = asAnySlice(strings.Split(value, f.Split))\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvalues = asAnySlice(strings.Fields(value))\n\t\t\t\t\t}\n\t\t\t\tcase []string:\n\t\t\t\t\tvalues = asAnySlice(value)\n\t\t\t\tcase []int:\n\t\t\t\t\tvalues = asAnySlice(value)\n\t\t\t\tcase []any:\n\t\t\t\t\tvalues = value\n\t\t\t\tcase map[string]any:\n\t\t\t\t\tfor k, v := range value {\n\t\t\t\t\t\tkeys = append(keys, k)\n\t\t\t\t\t\tvalues = append(values, v)\n\t\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\treturn nil, nil, errors.TaskfileInvalidError{\n\t\t\t\t\t\tURI: location.Taskfile,\n\t\t\t\t\t\tErr: errors.New(\"loop var must be a delimiter-separated string, list or a map\"),\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn values, keys, nil\n}\n\n// resolveMatrixRefs resolves any `ref:` rows in matrix and returns a new\n// Matrix with those rows populated. It must not mutate the matrix passed in:\n// that matrix is part of the shared, cached Task AST, and concurrent\n// invocations of the same task (e.g. via parallel deps) call this with the\n// same *ast.Matrix and would otherwise race on the row.Value assignment\n// below, intermittently leaking a value resolved for one caller into another\n// caller's expansion. See #2890.\nfunc resolveMatrixRefs(matrix *ast.Matrix, cache *templater.Cache) (*ast.Matrix, error) {\n\tif matrix.Len() == 0 {\n\t\treturn matrix, nil","sourceCodeStart":440,"sourceCodeEnd":476,"githubUrl":"https://github.com/go-task/task/blob/385e5ad92af02877b6d7cf9dcc963b5ed916e70a/variables.go#L440-L476","documentation":"Raised by itemsFromFor (called via compiledTask) when a `for` loop's `var` does not expand to one of the supported iterable shapes: a delimiter-separated string (e.g. comma-separated with `split:`), a list (Go/JSON-style array), or a map. When the resolved variable's underlying value is any other type, the executor returns this TaskfileInvalidError wrapping the message, tagged with the Taskfile location.","triggerScenarios":"A task uses `for: {var: NAME, ...}` where NAME resolves to a scalar/bool/other non-iterable value — e.g. the variable was set to a plain string without `split:` configured, to a number, to a command substitution result used directly without split, or the var name is misspelled so it resolves to an unexpected value.","commonSituations":"Typo in the variable name inside `for: var:`; forgetting `split: ','` when looping over a plain string; looping over a variable produced by a command that returns a single value instead of a list; YAML that quotes a list into a single string.","solutions":["Check what the variable actually resolves to (print it in a task cmd) and ensure it is a list or map","If looping a plain string, add `split:` under the `for` block, e.g. `for: {var: ITEMS, split: ','}`","Fix the variable definition so it is a YAML list/map, or the loop's var name typo","Verify YAML quoting so list values are not collapsed into one scalar string"],"exampleFix":"# before\nvars:\n  SERVICES: api web worker\ntasks:\n  build:\n    cmds:\n      - for: { var: SERVICES }\n        task: build-one\n# after\ntasks:\n  build:\n    cmds:\n      - for: { var: SERVICES, split: ' ' }\n        task: build-one","handlingStrategy":"validation","validationCode":"# Ensure loop variables are lists/maps or configure split before running task:\nvars:\n  SERVICES: [api, web, worker]\ncmds:\n  - for: { var: SERVICES }\n    task: build-one","typeGuard":null,"tryCatchPattern":"if err := e.Run(ctx, task, call); err != nil {\n    var terr *errors.TaskfileInvalidError\n    if errors.As(err, &terr) && strings.Contains(err.Error(), \"loop var must be\") {\n        return fmt.Errorf(\"fix `for: var:` in %s: %w\", terr.URI, err)\n    }\n    return err\n}","preventionTips":["Define loop sources as YAML lists or maps; never loop a bare scalar string without `split:`","Double-check the variable name in `for: var:` for typos against declared vars","If the var comes from a command substitution, pair it with `split:` or structure its output as a list","Run `task --list` or a dry run after Taskfile edits to catch loop-shape errors early"],"tags":["go-task","taskfile","for-loop","variable","configuration"],"backgroundTag":"invalid-loop-variable","analyzedSha":"385e5ad92af02877b6d7cf9dcc963b5ed916e70a","analyzedAt":"2026-09-05T09:01:05.226Z","contentChangedAt":"2026-09-05T09:01:05.226Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}