GitHub Actions join() / toJSON Misuse on Non-Array Values
join() concatenates the elements of an array with a separator. Passing a scalar, or an array of objects, yields empty or surprising output because there is nothing simple to join.
What this error means
A join() expression returns an empty string or a row of separators with no values, usually when applied to a single value or to objects rather than an array of strings.
.github/workflows/ci.yml
# matrix.os is a single string here, not an array - join produces nothing useful
run: echo "${{ join(matrix.os, ', ') }}"Common causes
join() called on a scalar
join() expects an array. Passing a single string or number returns that value unchanged or empty, not a comma-separated list.
Array of objects, not strings
join() stringifies array elements. An array of objects produces empty or Object placeholders because objects have no useful string form.
How to fix it
Join an actual array of strings
.github/workflows/ci.yml
# fromJSON produces a real array of strings
run: echo "${{ join(fromJSON('["linux","macos","windows"]'), ', ') }}"Pick a scalar field before joining
- If you have an array of objects, map to a scalar field first or build a string array.
- Use toJSON to inspect a value when you are unsure of its shape.
- For a single value, drop join() and reference the value directly.
How to prevent it
- Confirm a value is an array of scalars before calling join().
- Use toJSON to debug the actual shape of a context value.
- Build string arrays explicitly when you intend to join them.
Related guides
GitHub Actions contains() Always False - Wrong Argument TypeFix GitHub Actions contains() that never matches - passing a string where an array is expected, or comparing…
GitHub Actions Dynamic Matrix from fromJSON Fails - Not an ArrayFix GitHub Actions dynamic matrix errors - a matrix built from fromJSON of a job output that is empty, not va…
GitHub Actions format() Error - Placeholder Out of Range or Bad BracesFix GitHub Actions format() expression errors - a placeholder index with no matching argument, or literal bra…