Skip to content

fix(hook): macOS bash 3.2 compatibility (mapfile + empty-array under set -u)#9

Merged
MakFly merged 1 commit into
MakFly:mainfrom
mrfazolka:fix/macos-bash-3.2-mapfile
Jun 16, 2026
Merged

fix(hook): macOS bash 3.2 compatibility (mapfile + empty-array under set -u)#9
MakFly merged 1 commit into
MakFly:mainfrom
mrfazolka:fix/macos-bash-3.2-mapfile

Conversation

@mrfazolka

Copy link
Copy Markdown

Problem

The SessionStart hook crashes on macOS with session-start.sh: line 229: mapfile: command not found, producing a SessionStart:startup hook error on every Claude Code session start on a Mac.

mapfile/readarray is a bash 4.0+ builtin; macOS ships bash 3.2.57 as the system bash at /bin/bash, and #!/usr/bin/env bash resolves to it on a stock Mac.

Closes #8.

Changes

hooks/session-start.sh

  • Replace mapfile -t apps < <(...) with a portable while IFS= read -r loop — works on bash 3.2, 4 and 5.
  • Initialize local apps=() and guard if [[ ${#apps[@]} -gt 0 ]] before printf '%s\n' "${apps[@]}". Under set -u (the script uses set -euo pipefail), expanding an empty array as "${apps[@]}" raises apps[@]: unbound variable on bash < 4.4 — this was a second latent instance of the same class, surfacing whenever the hook runs outside a Symfony project.

.github/workflows/test-session-start.yml (regression guard — this is why the bug slipped through: CI was Linux-only / bash 5)

  • Run the test job on a [ubuntu-latest, macos-latest] matrix.
  • New step runs the hook under the system /bin/bash (3.2 on macOS) for both the empty-dir and mock-Symfony cases, and fails on any command not found / unbound variable / mapfile in output.
  • bash -n lint now also runs under /bin/bash where present.

Verification (local, macOS /bin/bash 3.2.57)

=== syntax (bash 3.2) ===            OK
=== empty dir ===                    PASS: clean (no stderr leak)
=== symfony dir ===                  PASS: valid JSON
=== unfixed upstream vs new guard === PASS: guard catches the bug

The new CI step was confirmed to fail against the current (unfixed) script and pass against this branch, so it genuinely guards the regression.

Notes

  • No behavior change on Linux — the read-loop and the count guard are equivalent to mapfile + unguarded expansion there.
  • No new dependencies; pure bash-portability fix.

mapfile is a bash 4.0+ builtin but macOS ships bash 3.2.57, so the
SessionStart hook died with "mapfile: command not found" on every Mac.

- Replace `mapfile -t apps` with a portable `while IFS= read -r` loop.
- Init `local apps=()` + guard ${#apps[@]} before "${apps[@]}" expansion,
  which raises "unbound variable" on empty arrays under bash 3.2 + set -u.
- CI: add macos-latest matrix + a step that runs the hook under system
  /bin/bash (3.2) and fails on command-not-found / unbound-variable.

Fixes MakFly#8
@zilionis

Copy link
Copy Markdown

BTW it will work if my main terminal is with zsh?

@MakFly

MakFly commented Jun 16, 2026

Copy link
Copy Markdown
Owner

Thanks for the thorough fix — and especially for adding the macOS / bash 3.2 regression guard in CI. That Linux-only matrix was exactly the gap that let this slip through.

Reviewed the diff: the while IFS= read -r loop replacing mapfile and the ${#apps[@]} guard before the printf are both correct and portable across bash 3.2/4/5, and running the hook under the system /bin/bash with the empty-dir + JSON assertions is a solid safety net.

Merging now — it'll ship in v0.0.1. Appreciate the clean write-up. 🙌

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SessionStart hook fails on macOS: mapfile: command not found (bash 3.2 incompatibility)

3 participants