Skip to content

SIGSEGV / panic in fingerprint from apostrophe in env var shell command filename #2753

Closed

Description

@loganfred

Description

My goal is to build a static website using go-task.

I have a hierarchy like this:

.
├── bin
├── output
│   └── public
│       └── html
│           ├── essays
│           └── notes
├── private
│   ├── resume
│   │   └── Taskfile.yml
│   └── Taskfile.yml
├── public
│   ├── essays
│   │   └── Taskfile.yml
│   ├── notes
│   │   └── Taskfile.yml
│   └── Taskfile.yml
└── Taskfile.yml

I want to use fingerprinting to avoid unnecessary work. I don't want conversion to HTML to happen sequentially because it will take forever.

The meat of my solution is as follows:

 compile:
    internal: true
    label: compile-essay-{{.FILE}}
    requires:
      vars: [FILE, OUT_DIR, OUT]
    sources:
      - '{{.FILE}}'
    generates:
      - '{{.OUT}}'
    cmds:
      - pandoc {{.PANDOC_FLAGS}} {{.FILE}} -o {{.OUT}}

  build-all:
    desc: Compile all .md files in parallel with fingerprinting
    silent: true
    vars:
      FILES:
        sh: find {{.SRC_DIR}} -name '*.md' -printf "%f\n"
    deps:
      - for:
          var: FILES
          split: "\n"
          as: FILE
        task: compile
        vars:
          FILE: '{{.FILE}}'
          OUT_DIR: '{{.OUT_DIR}}'
          OUT: '{{.OUT_DIR}}{{base .FILE | trimSuffix ".md"}}.html'

Since I have essays and notes folders, I copied this approach to Taskfile.yml files in both directories so that I can have distinct handling for the different files../bin/task public:essays worked fine, but ./bin/task public:notes would SIGSEGV.

Note that the label is different for essays and notes, the namespace is different, but the task is the same.

The root cause ended up being that both folders had index.md files. Deleting the latter file fixed the issue, changing the label did not. Renaming the file still created the error unless I modified the filetype. These files are in separate namespaces. I still don't really understand-- I would like for the fingerprinting to be based on the label or the namespace or the path. I'm still not quite sure what the workaround is yet.

EDIT: I'm still experiencing the issue so I was wrong about finding a fix. I think the state is getting tangled up somehow. I think using the same VARs in two separate dependencies tasks was causing issues. I switched them to cmds tasks and things are better, but something with the fingerprinting is still failing even if I add a clean step that finds all the .task folders and deletes them.

I'll have to dig into this more to figure out what's going wrong.

I did eventually find the note in the documentation about this but I don't think it should error this way and the logging could be improved.

https://taskfile.dev/docs/guide#by-fingerprinting-locally-generated-files-and-their-sources

Here is what the error looks like. The point that it happens is racey because the file order is different each time and there is no help from echo debugging or --verbose because I think the fingerprinting happens before all that.

(>10  successes)
...
task: [compile-note-200904-0627-change_youtube_speed.md] pandoc --lua-filter=fix-links.lua '200904-0627-change_youtube_speed.md' -o '../../output/public/html/notes/200904-0627-change_youtube_speed.html'

task: [compile-note-240904-1055-mildred.md] pandoc --lua-filter=fix-links.lua '240904-1055-mildred.md' -o '../../output/public/html/notes/240904-1055-mildred.html'

task: [compile-note-20230903-1359_AUTOMATED_way_station_clifford_simak_hugo_award.md] pandoc --lua-filter=fix-links.lua '20230903-1359_AUTOMATED_way_station_clifford_simak_hugo_award.md' -o '../../output/public/html/notes/20230903-1359_AUTOMATED_way_station_clifford_simak_hugo_award.html'

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x62671b]

goroutine 478 [running]:
mvdan.cc/sh/v3/expand.FieldsSeq.func1(0x131e5e200e40)
	mvdan.cc/sh/v3@v3.12.1-0.20260124232039-e74afc18e65b/expand/expand.go:459 +0x9b
mvdan.cc/sh/v3/expand.Fields(0x131e5e0ce140, {0x131e5c282508, 0x1, 0x1})
	mvdan.cc/sh/v3@v3.12.1-0.20260124232039-e74afc18e65b/expand/expand.go:442 +0x125
github.com/go-task/task/v3/internal/execext.ExpandFields({0x131e5e0aa1e0?, 0x39?})
	github.com/go-task/task/v3@v3.49.1/internal/execext/exec.go:139 +0x18e
github.com/go-task/task/v3/internal/fingerprint.glob({0x131e5cf1a980?, 0x131e5d833570?}, {0x131e5c2d9080?, 0x18?})
	github.com/go-task/task/v3@v3.49.1/internal/fingerprint/glob.go:29 +0x37
github.com/go-task/task/v3/internal/fingerprint.Globs({0x131e5cf1a980, 0x39}, {0x131e5c2823c0, 0x1, 0x0?})
	github.com/go-task/task/v3@v3.49.1/internal/fingerprint/glob.go:15 +0xfb
github.com/go-task/task/v3/internal/fingerprint.(*ChecksumChecker).checksum(0x425d85?, 0x18?)
	github.com/go-task/task/v3@v3.49.1/internal/fingerprint/sources_checksum.go:91 +0x45
github.com/go-task/task/v3/internal/fingerprint.(*ChecksumChecker).Value(0x0?, 0x131e5da93080?)
	github.com/go-task/task/v3@v3.49.1/internal/fingerprint/sources_checksum.go:76 +0x13
github.com/go-task/task/v3.(*Executor).compiledTask(0x131e5c638f00, 0x131e5c6056e0, 0x0)
	github.com/go-task/task/v3@v3.49.1/variables.go:193 +0xfd4
github.com/go-task/task/v3.(*Executor).FastCompiledTask(...)
	github.com/go-task/task/v3@v3.49.1/variables.go:30
github.com/go-task/task/v3.(*Executor).RunTask(0x131e5c638f00, {0x209ae80, 0x3385860}, 0x131e5c6056e0)
	github.com/go-task/task/v3@v3.49.1/task.go:142 +0x1a8
github.com/go-task/task/v3.(*Executor).runDeps.func1()
	github.com/go-task/task/v3@v3.49.1/task.go:329 +0x9a
golang.org/x/sync/errgroup.(*Group).Go.func1()
	golang.org/x/sync@v0.19.0/errgroup/errgroup.go:93 +0x50
created by golang.org/x/sync/errgroup.(*Group).Go in goroutine 1
	golang.org/x/sync@v0.19.0/errgroup/errgroup.go:78 +0x95

Version

3.49.1

Operating system

Linux ARCH 6.19.8-arch1-1 #1 SMP PREEMPT_DYNAMIC Sat, 14 Mar 2026 01:07:43 +0000 x86_64 GNU/Linux

Experiments Enabled

No response

Example Taskfile

# yaml-language-server: $schema=https://taskfile.dev/schema.json

# copy this file into two separate directories at the same level, add them as includes, then execute as cmd tasks

version: '3'

vars:
  OUT_DIR: ../../output/public/html/notes/

tasks:
  default:
    vars:
      FILES:
        sh: find . -name '*.md' -printf "%f\n"
    deps:
      - for:
          var: FILES
          split: "\n"
          as: FILE
        task: compile
        vars:
          FILE: '{{.FILE}}'
          OUT_DIR: '{{.OUT_DIR}}'

  compile:
    internal: true
    label: 'compile-{{.FILE}}'
    requires:
      vars: [FILE, OUT_DIR]
    sources:
      - '{{.FILE}}'
    generates:
      - '{{.OUT_DIR}}/{{base .FILE | trimSuffix ".md"}}.html'
    cmds:
      - >
        pandoc --lua-filter=fix-links.lua
        -o '{{.OUT_DIR}}/{{base .FILE | trimSuffix ".md"}}.html'
        '{{.FILE}}'

Activity

  1. changed the title [-]SIGSEGV in fingerprint if duplicate filenames in different directories[/-] [+]SIGSEGV in fingerprint possibly due to duplicate filenames throughout different dirs and namespaces[/+] on Mar 20, 2026
  2. timrulebosch commented on Mar 20, 2026

    @timrulebosch

    Is failing while expanding sources. You could try this to understand the glob pattern that causes the crash:

    compile:
        internal: true
        label: compile-essay-{{.FILE}}
        requires:
          vars: [FILE, OUT_DIR, OUT]
        cmds:
          - echo {{.FILE}}
    

    That should not crash, and may give a clue as to what is happening.

  3. loganfred commented on Mar 21, 2026

    @loganfred
    Author

    I updated the public:essays:compile and public:notes:compile like so:

    compile:
        internal: true
        label: 'compile-note-{{.FILE}}'
        requires:
          vars: [FILE, OUT_DIR, OUT]
        #sources:
        #  - '{{.FILE}}'
        #generates:
        #  - '{{.OUT}}'
        #cmds:
        #  - pandoc {{.PANDOC_FLAGS}} {{.FILE}} -o {{.OUT}}
      cmds:
        - echo {{.FILE}}
    

    Looks like the build-all -> deps -> FILES var is only run once, for the public:essays:compile, and not for public:notes:compile
    Could that be it? I don't feel like I have the right mental model of scoping.

    Here is the output

    [USER@ARCH test_repo]$ go-task -a
    task: Available tasks for this project:
    * all:
    * clean:
    * private:all:
    * private:resume:default:        Compile My Resume      (aliases: private:resume)
    * public:all:
    * public:essays:build-all:       Compile all .md files in parallel with fingerprinting      (aliases: public:essays:essays)
    * public:essays:clean:           Remove output directory
    * public:essays:cmds:
    * public:essays:mkdir-out:
    * public:notes:build-all:        Compile all .md files in parallel with fingerprinting      (aliases: public:notes:notes)
    * public:notes:clean:            Remove output directory
    * public:notes:cmds:
    * public:notes:mkdir-out:
    
    [USER@ARCH test_repo]$ go-task clean
    task: [clean] rm -rf output
    task: [clean] find . -name '.task' -exec -I {} rm -r {} \;
    
    [USER@ARCH test_repo]$ go-task all -f --verbose --output=group
    task: "all" started
    task: "private:all" started
    task: "public:all" started
    task: "private:resume" started
    task: [private:resume:default] mkdir -p "../../output/private/resume"
    task: dynamic variable: "find . -name '*.md' -printf \"%f\\n\"" result: "FNAME_1.md\nFNAME_2.md\nFNAME_3.md\nFNAME_4.md\nFNAME_5.md\nFNAME_6.md\nFNAME_7.md\nFNAME_8.md\nFNAME_9.md\nFNAME_10.md\nFNAME_11.md\nFNAME_12.md\nFNAME_13.md\nFNAME_14.md\nFNAME_15.md\nFNAME_16.md\nFNAME_17.md\nFNAME_18.md\nFNAME_19.md\nFNAME_20.md\nFNAME_21.md\nFNAME_22.md\nFNAME_23.md\nFNAME_24.md\nFNAME_25.md\nFNAME_26.md\nFNAME_27.md\nFNAME_28.md\nFNAME_29\n.md"
    task: [private:resume:default] pdflatex -output-directory="../../output/private/resume" resume.tex 2>&1
    task: "public:essays:build-all" started
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" started
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" finished
    task: "public:essays:compile" finished
    task: "public:essays:mkdir-out" started
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: [public:essays:mkdir-out] mkdir -p ../../output/public/html/notes
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:compile" started
    task: "public:essays:compile" finished
    task: "public:essays:mkdir-out" finished
    task: "public:essays:build-all" finished
    task: "public:notes:build-all" started
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" finished
    task: "public:notes:mkdir-out" started
    task: [public:notes:mkdir-out] mkdir -p ../../output/public/html/notes
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:compile" started
    task: "public:notes:compile" finished
    task: "public:notes:mkdir-out" finished
    task: "public:notes:build-all" finished
    task: "public:all" finished
    This is pdfTeX, Version 3.141592653-2.6-1.40.29 (TeX Live 2026/Arch Linux) (preloaded format=pdflatex)
     restricted \write18 enabled.
    entering extended mode
    (./resume.tex
    LaTeX2e <2025-11-01>
    L3 programming layer <2026-01-19>
    (/usr/share/texmf-dist/tex/latex/base/article.cls
    Document Class: article 2025/01/22 v1.4n Standard LaTeX document class
    (/usr/share/texmf-dist/tex/latex/base/size10.clo))
    (/usr/share/texmf-dist/tex/latex/enumitem/enumitem.sty)
    (/usr/share/texmf-dist/tex/latex/titlesec/titlesec.sty)
    (/usr/share/texmf-dist/tex/latex/titling/titling.sty)
    (/usr/share/texmf-dist/tex/latex/geometry/geometry.sty
    (/usr/share/texmf-dist/tex/latex/graphics/keyval.sty)
    (/usr/share/texmf-dist/tex/generic/iftex/ifvtex.sty
    (/usr/share/texmf-dist/tex/generic/iftex/iftex.sty)))
    (/usr/share/texmf-dist/tex/latex/amsmath/amsmath.sty
    For additional information on amsmath, use the `?' option.
    (/usr/share/texmf-dist/tex/latex/amsmath/amstext.sty
    (/usr/share/texmf-dist/tex/latex/amsmath/amsgen.sty))
    (/usr/share/texmf-dist/tex/latex/amsmath/amsbsy.sty)
    (/usr/share/texmf-dist/tex/latex/amsmath/amsopn.sty))
    (/usr/share/texmf-dist/tex/latex/amsfonts/amssymb.sty
    (/usr/share/texmf-dist/tex/latex/amsfonts/amsfonts.sty))
    (/usr/share/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def)
    No file resume.aux.
    *geometry* driver: auto-detecting
    *geometry* detected driver: pdftex
    (/usr/share/texmf-dist/tex/latex/amsfonts/umsa.fd)
    (/usr/share/texmf-dist/tex/latex/amsfonts/umsb.fd)
    Overfull \hbox (17.2222pt too wide) in paragraph at lines 74--94
    [][] []
    [1{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}]
    (../../output/private/resume/resume.aux) )
    (see the transcript file for additional information)</usr/share/texmf-dist/font
    s/type1/public/amsfonts/cm/cmbx10.pfb></usr/share/texmf-dist/fonts/type1/public
    /amsfonts/cm/cmbx12.pfb></usr/share/texmf-dist/fonts/type1/public/amsfonts/cm/c
    mr10.pfb></usr/share/texmf-dist/fonts/type1/public/amsfonts/cm/cmr12.pfb></usr/
    share/texmf-dist/fonts/type1/public/amsfonts/cm/cmr17.pfb></usr/share/texmf-dis
    t/fonts/type1/public/amsfonts/cm/cmsy10.pfb></usr/share/texmf-dist/fonts/type1/
    public/amsfonts/cm/cmti10.pfb></usr/share/texmf-dist/fonts/type1/public/amsfont
    s/cm/cmti12.pfb>
    Output written on ../../output/private/resume/resume.pdf (1 page, 126865 bytes)
    .
    Transcript written on ../../output/private/resume/resume.log.
    task: "private:resume" finished
    task: "private:all" finished
    task: "all" finished
    

    And here are the task files used.

    find . -name 'Taskfile.yml' -exec sh -c 'echo "\n=== {} ===\n"; cat "{}"' \;
    
    === ./Taskfile.yml ===
    
    # yaml-language-server: $schema=https://taskfile.dev/schema.json
    
    version: '3'
    
    includes:
      public:
        taskfile: ./public/Taskfile.yml
        dir: ./public
      private:
        taskfile: ./private/Taskfile.yml
        dir: ./private
    
    tasks:
      all:
        deps:
          - public:all
          - private:all
      clean:
        cmds:
          - rm -rf output
          - find . -name '.task' -exec -I {} rm -r {} \;
    
    === ./public/Taskfile.yml ===
    
    # yaml-language-server: $schema=https://taskfile.dev/schema.json
    
    version: '3'
    
    includes:
      essays:
        taskfile: ./essays/Taskfile.yml
        dir: ./essays
      notes:
        taskfile: ./notes/Taskfile.yml
        dir: ./notes
    
    tasks:
      all:
        cmds:
          - task: essays:build-all
          - task: notes:build-all
    
    === ./private/Taskfile.yml ===
    
    # yaml-language-server: $schema=https://taskfile.dev/schema.json
    
    version: '3'
    
    includes:
      resume:
        taskfile: ./resume/Taskfile.yml
        dir: ./resume
    
    tasks:
      all:
        silent: true
        deps:
        - resume
    
    === ./private/resume/Taskfile.yml ===
    
    # yaml-language-server: $schema=https://taskfile.dev/schema.json
    
    version: '3'
    
    output:
      group:
        error_only: true
    
    vars:
      TASK_OUTPUT: ../../output/private/resume
    
    tasks:
      default:
        silent: true
        desc: Compile My Resume
        cmds:
          - mkdir -p "{{.TASK_OUTPUT}}"
          - pdflatex -output-directory="{{.TASK_OUTPUT}}" resume.tex 2>&1
    
    === ./public/notes/Taskfile.yml ===
    
    # yaml-language-server: $schema=https://taskfile.dev/schema.json
    
    version: '3'
    
    vars:
      OUT_DIR: ../../output/public/html/notes
      PANDOC_FLAGS: --highlight-style=pygments --lua-filter=fix-links.lua
      SRC_DIR: .
    
    tasks:
    
      mkdir-out:
        cmds:
          - mkdir -p {{.OUT_DIR}}
        status:
          - test -d {{.OUT_DIR}}
    
      compile:
        internal: true
        label: 'compile-note-{{.FILE}}'
        requires:
          vars: [FILE, OUT_DIR, OUT]
        #sources:
        #  - '{{.FILE}}'
        #generates:
        #  - '{{.OUT}}'
        #cmds:
        #  - pandoc {{.PANDOC_FLAGS}} {{.FILE}} -o {{.OUT}}
      cmds:
        - echo {{.FILE}}
    
      build-all:
        desc: Compile all .md files in parallel with fingerprinting
        aliases: [notes]
        vars:
          FILES:
            sh: find {{.SRC_DIR}} -name '*.md' -printf "%f\n"
        deps:
          - task: mkdir-out
          - for:
              var: FILES
              split: "\n"
              as: FILE
            task: compile
            vars:
              FILE: '{{.FILE}}'
              OUT_DIR: '{{.OUT_DIR}}'
              OUT: '{{.OUT_DIR}}/{{base .FILE | trimSuffix ".md"}}.html'
      clean:
        desc: Remove output directory
        cmds:
          - rm -rf {{.OUT_DIR}}
    
    === ./public/essays/Taskfile.yml ===
    
    # yaml-language-server: $schema=https://taskfile.dev/schema.json
    
    version: '3'
    
    vars:
      OUT_DIR: ../../output/public/html/essays
      PANDOC_FLAGS: --highlight-style=pygments --lua-filter=fix-links.lua
      SRC_DIR: .
    
    tasks:
    
      mkdir-out:
        cmds:
          - mkdir -p {{.OUT_DIR}}
        status:
          - test -d {{.OUT_DIR}}
    
      compile:
        internal: true
        label: 'compile-essay-{{.FILE}}'
        requires:
          vars: [FILE, OUT_DIR, OUT]
        #sources:
        #  - '{{.FILE}}'
        #generates:
        #  - '{{.OUT}}'
        #cmds:
        #  - pandoc {{.PANDOC_FLAGS}} {{.FILE}} -o {{.OUT}}
      cmds:
        - echo {{.FILE}}
    
      build-all:
        desc: Compile all .md files in parallel with fingerprinting
        aliases: [essays]
        vars:
          FILES:
            sh: find {{.SRC_DIR}} -name '*.md' -printf "%f\n"
        deps:
          - task: mkdir-out
          - for:
              var: FILES
              split: "\n"
              as: FILE
            task: compile
            vars:
              FILE: '{{.FILE}}'
              OUT_DIR: '{{.OUT_DIR}}'
              OUT: '{{.OUT_DIR}}/{{base .FILE | trimSuffix ".md"}}.html'
      clean:
        desc: Remove output directory
        cmds:
          - rm -rf {{.OUT_DIR}}
    
  4. trulede commented on Mar 22, 2026

    @trulede
    Contributor

    @loganfred the sources is causing the crash, so you need to capture that in the log (you commented it out). It should not crash.

    Its helpful to have a reliable way to reproduce the problem. So if we discover the exact trigger of that ... helpful.

    I do similar things with Task, so this should work. The error itself indicates something in particular, but need to know the condition which triggers it to understand why.

  5. loganfred commented on Mar 22, 2026

    @loganfred
    Author

    I found some time to switch to a minimal example.

    I think I'm seeing that explicit dir: is not being honored for multiple includes:
    As deps: which dir is a race and as cmds: the first dir is taken for all

    I haven't worked up to sources: yet. I would guess that the fingerprinting is all messed up each time I switch from the leaf node tasks (which work fine) to running in parallel (deps) or sequence (cmds).

    bug-repro/
    ├── Taskfile.yml
    ├── alpha/
    │   ├── Taskfile.yml
    │   ├── one.txt
    │   └── two.txt
    └── beta/
        ├── Taskfile.yml
        ├── three.txt
        └── four.txt
    

    Root Taskfile.yml:

    version: '3'
    
    includes:
      alpha:
        taskfile: ./alpha/Taskfile.yml
        dir: ./alpha
      beta:
        taskfile: ./beta/Taskfile.yml
        dir: ./beta
    
    tasks:
      all:
        cmds:
          - task: alpha:show
          - task: beta:show
      all-parallel:
        deps:
          - alpha:show
          - beta:show

    alpha/Taskfile.yml:

    version: '3'
    
    vars:
      MY_VAR: ALPHA
      SRC_DIR: .
    
    tasks:
      show:
        vars:
          FILES:
            sh: find {{.SRC_DIR}} -name '*.txt' -printf "%f\n"
        cmds:
          - echo "MY_VAR={{.MY_VAR}}"
          - echo "SRC_DIR={{.SRC_DIR}}"
          - echo "FILES={{.FILES}}"

    beta/Taskfile.yml: (identical structure, MY_VAR: BETA)

    version: '3'
    
    vars:
      MY_VAR: BETA
      SRC_DIR: .
    
    tasks:
      show:
        vars:
          FILES:
            sh: find {{.SRC_DIR}} -name '*.txt' -printf "%f\n"
        cmds:
          - echo "MY_VAR={{.MY_VAR}}"
          - echo "SRC_DIR={{.SRC_DIR}}"
          - echo "FILES={{.FILES}}"

    Expected behavior

    Each included taskfile's sh: var should execute relative to the dir: specified in its includes: block.

    # go-task all
    alpha:show -> MY_VAR=ALPHA  FILES=one.txt, two.txt
    beta:show  -> MY_VAR=BETA   FILES=three.txt, four.txt
    

    Actual behavior

    Sequential (go-task all) — beta's sh: command runs in alpha's directory:

    alpha:show -> MY_VAR=ALPHA  FILES=one.txt, two.txt
    beta:show  -> MY_VAR=BETA   FILES=one.txt, two.txt
    

    Parallel (go-task all-parallel) — race condition, both get the same files:

    alpha:show -> MY_VAR=ALPHA  FILES=three.txt, four.txt
    beta:show  -> MY_VAR=BETA   FILES=three.txt, four.txt
    
  6. loganfred commented on Mar 22, 2026

    @loganfred
    Author

    FWIW the bots suggest review in these areas:

    taskfile/ask/taskfile.go:70-72 may have a copy-paste error where t1.vars are being merged into t1 instead of t2.vars

    compiler.go:160 may be caching dynamic vars by shell string, which may not permit re-using the same shell commands to create dynamic variables.

    taskfile/ask/tasks.go:179 may not allow tasks that already got their vars from a nested include to keep them

    I have a patch I can PR if it would be helpful. It passes my minimal example, solves the real-world problem, and the test suite passes, but I'll be honest I'm not a go programmer it was 3% me and 97% claude. I just wanted a workaround to keep tinkering.

  7. changed the title [-]SIGSEGV in fingerprint possibly due to duplicate filenames throughout different dirs and namespaces[/-] [+]SIGSEGV in fingerprint; explicit dir include dynamic vars are not isolated and caching prohibits exact shell command re-use[/+] on Mar 22, 2026
  8. loganfred commented on Mar 23, 2026

    @loganfred
    Author

    Ugh, I'm still getting the SIGSEGV actually, even with the patched binary that isolates the dynamic variables.

    I'll try to learn how to use delve so I can see what's happening and report back.

  9. trulede commented on Mar 23, 2026

    @trulede
    Contributor

    The 'dir' problem, possibly a clear bug. Just a bit surprised no one else has noticed.

    The 'files' ... I would think, with the dir problem fixed, that you would not see any problem with the sequential task calls.

    For the dep case, the tasks get compiled first. Could be a problem there if for some reason the templater cache was shared between them.

    You could try to form the sources differently, and see if that prevents the problem. That would be a proof. For instance "{{.SRC_DIR}}" and "./{{.SRC_DIR}}".

  10. trulede commented on Mar 23, 2026

    @trulede
    Contributor

    With this setup I don't observe the directory problem. Be careful with global vars (taskfile level) when importing, I suggest not doing that. Try hardcoding or task level vars.

    This bit, move them to the task and see if it helps.

    vars:
      MY_VAR: BETA
      SRC_DIR: .
    

    I will try the file listing a bit later.

    $ cat Taskfile.yml
    version: '3'
    
    includes:
      alpha:
        taskfile: ./alpha/Taskfile.yml
        dir: ./alpha
      beta:
        taskfile: ./beta/Taskfile.yml
        dir: ./beta
    
    tasks:
      seq:
        cmds:
          - task: alpha:info
          - task: beta:info
      dep:
        deps:
          - alpha:info
          - beta:info
    
    $ cat alpha/Taskfile.yml
    version: '3'
    
    tasks:
      info:
        cmds:
          - echo "TASK={{.TASK}}"
          - echo "TASK_DIR={{.TASK_DIR}}"
          - echo "PWD=$(pwd)"
    $ cat beta/Taskfile.yml
    version: '3'
    
    tasks:
      info:
        cmds:
          - echo "TASK={{.TASK}}"
          - echo "TASK_DIR={{.TASK_DIR}}"
          - echo "PWD=$(pwd)
    
    task dep
    task: [alpha:info] echo "TASK=alpha:info"
    task: [beta:info] echo "TASK=beta:info"
    TASK=alpha:info
    task: [alpha:info] echo "TASK_DIR=/home/git/task/ISSUES/2757/alpha"
    TASK=beta:info
    task: [beta:info] echo "TASK_DIR=/home/git/task/ISSUES/2757/beta"
    TASK_DIR=/home/git/task/ISSUES/2757/beta
    task: [beta:info] echo "PWD=$(pwd)"
    TASK_DIR=/home/git/task/ISSUES/2757/alpha
    task: [alpha:info] echo "PWD=$(pwd)"
    PWD=/home/git/task/ISSUES/2757/alpha
    PWD=/home/git/task/ISSUES/2757/beta
    
  11. loganfred commented on Mar 23, 2026

    @loganfred
    Author

    With this setup I don't observe the directory problem.

    I think it has to do with include: since I ran the simpler scenario from the previous message on my machine and it worked as expected.

    Try hardcoding or task level vars.

    I re-ran my minimalist example. 3.49.1+aa83651d is producing the same result with task level vars as with the global vars.
    Hard-coding does fix the dir: with include: issue, I used ../alpha and ../beta as arguments to find

    Output from:

    1. 3.49.1+aa83651d version with global vars (bug result)
    2. Patched Version with global vars (expected result)
    3. 3.49.1+aa83651d version with local vars (bug result)
    4. 3.49.1+aa83651d version ../alpha and ../beta for distinct paths (expected result)

    And the Taskfile.yml files

    3.49.1+aa83651d version with global vars (bug result)

    [USER@ARCH bug-repro]$ go-task all --output=prefixed
    task: [alpha:show] echo "MY_VAR=ALPHA"
    [alpha:show] MY_VAR=ALPHA
    task: [alpha:show] echo "SRC_DIR=."
    [alpha:show] SRC_DIR=.
    task: [alpha:show] echo "FILES=one.txt
    two.txt"
    [alpha:show] FILES=one.txt
    [alpha:show] two.txt
    task: [beta:show] echo "MY_VAR=BETA"
    [beta:show] MY_VAR=BETA
    task: [beta:show] echo "SRC_DIR=."
    [beta:show] SRC_DIR=.
    task: [beta:show] echo "FILES=one.txt
    two.txt"
    [beta:show] FILES=one.txt
    [beta:show] two.txt
    [USER@ARCH bug-repro]$ go-task all-parallel --output=prefixed
    task: [alpha:show] echo "MY_VAR=ALPHA"
    task: [beta:show] echo "MY_VAR=BETA"
    [alpha:show] MY_VAR=ALPHA
    task: [alpha:show] echo "SRC_DIR=."
    [beta:show] MY_VAR=BETA
    task: [beta:show] echo "SRC_DIR=."
    [alpha:show] SRC_DIR=.
    task: [alpha:show] echo "FILES=one.txt
    two.txt"
    [beta:show] SRC_DIR=.
    task: [beta:show] echo "FILES=one.txt
    two.txt"
    [alpha:show] FILES=one.txt
    [alpha:show] two.txt
    [beta:show] FILES=one.txt
    [beta:show] two.txt
    

    Patched Version with global vars (expected result)

    [USER@ARCH bug-repro]$ ../go-task-patched all --output=prefixed
    task: [alpha:show] echo "MY_VAR=ALPHA"
    [alpha:show] MY_VAR=ALPHA
    task: [alpha:show] echo "SRC_DIR=."
    [alpha:show] SRC_DIR=.
    task: [alpha:show] echo "FILES=one.txt
    two.txt"
    [alpha:show] FILES=one.txt
    [alpha:show] two.txt
    task: [beta:show] echo "MY_VAR=BETA"
    [beta:show] MY_VAR=BETA
    task: [beta:show] echo "SRC_DIR=."
    [beta:show] SRC_DIR=.
    task: [beta:show] echo "FILES=four.txt
    three.txt"
    [beta:show] FILES=four.txt
    [beta:show] three.txt
    [USER@ARCH bug-repro]$ ../go-task-patched all-parallel --output=prefixed
    task: [alpha:show] echo "MY_VAR=ALPHA"
    task: [beta:show] echo "MY_VAR=BETA"
    [alpha:show] MY_VAR=ALPHA
    task: [alpha:show] echo "SRC_DIR=."
    [beta:show] MY_VAR=BETA
    task: [beta:show] echo "SRC_DIR=."
    [alpha:show] SRC_DIR=.
    task: [alpha:show] echo "FILES=one.txt
    two.txt"
    [beta:show] SRC_DIR=.
    task: [beta:show] echo "FILES=four.txt
    three.txt"
    [alpha:show] FILES=one.txt
    [alpha:show] two.txt
    [beta:show] FILES=four.txt
    [beta:show] three.txt
    

    3.49.1+aa83651d version with local vars (bug result)

    [USER@ARCH bug-repro]$ go-task all --output=prefixed
    task: [alpha:show] echo "MY_VAR=ALPHA"
    [alpha:show] MY_VAR=ALPHA
    task: [alpha:show] echo "SRC_DIR=."
    [alpha:show] SRC_DIR=.
    task: [alpha:show] echo "FILES=one.txt
    two.txt"
    [alpha:show] FILES=one.txt
    [alpha:show] two.txt
    task: [beta:show] echo "MY_VAR=BETA"
    [beta:show] MY_VAR=BETA
    task: [beta:show] echo "SRC_DIR=."
    [beta:show] SRC_DIR=.
    task: [beta:show] echo "FILES=one.txt
    two.txt"
    [beta:show] FILES=one.txt
    [beta:show] two.txt
    [USER@ARCH bug-repro]$ go-task all-parallel --output=prefixed
    task: [beta:show] echo "MY_VAR=BETA"
    task: [alpha:show] echo "MY_VAR=ALPHA"
    [beta:show] MY_VAR=BETA
    task: [beta:show] echo "SRC_DIR=."
    [alpha:show] MY_VAR=ALPHA
    task: [alpha:show] echo "SRC_DIR=."
    [beta:show] SRC_DIR=.
    [alpha:show] SRC_DIR=.
    task: [beta:show] echo "FILES=four.txt
    three.txt"
    task: [alpha:show] echo "FILES=four.txt
    three.txt"
    [beta:show] FILES=four.txt
    [beta:show] three.txt
    [alpha:show] FILES=four.txt
    [alpha:show] three.txt
    
    

    3.49.1+aa83651d version ../alpha and ../beta for distinct paths (expected result)

    [USER@ARCH bug-repro]$ go-task all --output=prefixed
    task: [alpha:show] echo "MY_VAR=ALPHA"
    [alpha:show] MY_VAR=ALPHA
    task: [alpha:show] echo "SRC_DIR=."
    [alpha:show] SRC_DIR=.
    task: [alpha:show] echo "FILES=one.txt
    two.txt"
    [alpha:show] FILES=one.txt
    [alpha:show] two.txt
    task: [beta:show] echo "MY_VAR=BETA"
    [beta:show] MY_VAR=BETA
    task: [beta:show] echo "SRC_DIR=."
    [beta:show] SRC_DIR=.
    task: [beta:show] echo "FILES=four.txt
    three.txt"
    [beta:show] FILES=four.txt
    [beta:show] three.txt
    [USER@ARCH bug-repro]$ go-task all-parallel --output=prefixed
    task: [alpha:show] echo "MY_VAR=ALPHA"
    task: [beta:show] echo "MY_VAR=BETA"
    [alpha:show] MY_VAR=ALPHA
    task: [alpha:show] echo "SRC_DIR=."
    [beta:show] MY_VAR=BETA
    task: [beta:show] echo "SRC_DIR=."
    [alpha:show] SRC_DIR=.
    task: [alpha:show] echo "FILES=one.txt
    two.txt"
    [beta:show] SRC_DIR=.
    task: [beta:show] echo "FILES=four.txt
    three.txt"
    [alpha:show] FILES=one.txt
    [alpha:show] two.txt
    [beta:show] FILES=four.txt
    [beta:show] three.txt
    
    
    ==> Taskfile.yml <==
    Version: '3'
    
    includes:
      alpha:
        taskfile: ./alpha/Taskfile.yml
        dir: ./alpha
      beta:
        taskfile: ./beta/Taskfile.yml
        dir: ./beta
    
    tasks:
      all:
        desc: "sequential via cmds — bug reproduces"
        cmds:
          - task: alpha:show
          - task: beta:show
      all-parallel:
        desc: "parallel via deps — also test this path"
        deps:
          - alpha:show
          - beta:show
    
    ==> alpha/Taskfile.yml <==
    version: '3'
    
    #vars:
    #  MY_VAR: ALPHA
    #  SRC_DIR: .
    
    tasks:
      show:
        vars:
          MY_VAR: ALPHA
          SRC_DIR: .
          FILES:
            sh: find {{.SRC_DIR}} -name '*.txt' -printf "%f\n"
        cmds:
          - echo "MY_VAR={{.MY_VAR}}"
          - echo "SRC_DIR={{.SRC_DIR}}"
          - echo "FILES={{.FILES}}"
    
    ==> beta/Taskfile.yml <==
    version: '3'
    
    #vars:
    #  MY_VAR: BETA
    #  SRC_DIR: .
    
    tasks:
      show:
        vars:
          MY_VAR: BETA
          SRC_DIR: .
          FILES:
            sh: find {{.SRC_DIR}} -name '*.txt' -printf "%f\n"
        cmds:
          - echo "MY_VAR={{.MY_VAR}}"
          - echo "SRC_DIR={{.SRC_DIR}}"
          - echo "FILES={{.FILES}}"
  12. loganfred commented on Mar 24, 2026

    @loganfred
    Author

    AHA, the segfault is unrelated.

    Here is the summary from my side so far:

    1. if you have multiple include:-ed tasks, each with their own dir:, those tasks aren't running from the correct folders.
    2. dir: {{.FOLDER}} may not work
    3. included: tasks that re-use the same exact sh: VAR (e.g. find . -name '*.md') in a different dir: may get the cache value from another task
    4. in a root -> parent -> A / B situation with nested includes, the outer merge may be clobbering existing inner merges
    5. apostrophes in sh: VARS create segfaults (e.g. pandoc chesterton's_fence.md)

    Shame on me for overlooking a stupid filename, but it probably shouldn't segfault

    #2757 seems to fix 1, 2, 3, 4

    Segfault minimal reproduction

    repro/
    ├── Taskfile.yml
    └── chesterton's_fence.txt    # any file with a ' in the name
    
      version: '3'
    
      tasks:
        show:
          vars:
            FILES:
              sh: find . -name '*.txt' -printf "%f\n"
          deps:
            - for:
                var: FILES
                split: "\n"
                as: FILE
              task: process
              vars:
                FILE: '{{.FILE}}'
    
        process:
          internal: true
          label: 'process-{{.FILE}}'
          requires:
            vars: [FILE]
          sources:
            - '{{.FILE}}'
          cmds:
            - echo "{{.FILE}}"
    [USER@ARCH bug-repro]$ go-task quote-segfault:show
    panic: runtime error: invalid memory address or nil pointer dereference
    [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x55cb1eb73b3b]
    
    goroutine 71 [running]:
    mvdan.cc/sh/v3/expand.FieldsSeq.func1(0x1d2ed38cb1d0)
    	/build/go-task/src/pkg/mod/mvdan.cc/sh/v3@v3.12.1-0.20260124232039-e74afc18e65b/expand/expand.go:459 +0x9b
    mvdan.cc/sh/v3/expand.Fields(0x1d2ed3a82500, {0x1d2ed3b02a68, 0x1, 0x1})
    	/build/go-task/src/pkg/mod/mvdan.cc/sh/v3@v3.12.1-0.20260124232039-e74afc18e65b/expand/expand.go:442 +0x125
    github.com/go-task/task/v3/internal/execext.ExpandFields({0x1d2ed37a64e0?, 0x45?})
    	/build/go-task/src/go-task/internal/execext/exec.go:139 +0x18e
    github.com/go-task/task/v3/internal/fingerprint.glob({0x1d2ed4098960?, 0x1d2ed3d3d570?}, {0x1d2ed4096390?, 0x18?})
    	/build/go-task/src/go-task/internal/fingerprint/glob.go:29 +0x37
    github.com/go-task/task/v3/internal/fingerprint.Globs({0x1d2ed4098960, 0x45}, {0x1d2ed3b02990, 0x1, 0x0?})
    	/build/go-task/src/go-task/internal/fingerprint/glob.go:15 +0xfb
    github.com/go-task/task/v3/internal/fingerprint.(*ChecksumChecker).checksum(0x55cb1e9661a5?, 0x18?)
    	/build/go-task/src/go-task/internal/fingerprint/sources_checksum.go:91 +0x45
    github.com/go-task/task/v3/internal/fingerprint.(*ChecksumChecker).Value(0x0?, 0x1d2ed3b255a0?)
    	/build/go-task/src/go-task/internal/fingerprint/sources_checksum.go:76 +0x13
    github.com/go-task/task/v3.(*Executor).compiledTask(0x1d2ed3a2d680, 0x1d2ed3b243c0, 0x0)
    	/build/go-task/src/go-task/variables.go:193 +0xfd4
    github.com/go-task/task/v3.(*Executor).FastCompiledTask(...)
    	/build/go-task/src/go-task/variables.go:30
    github.com/go-task/task/v3.(*Executor).RunTask(0x1d2ed3a2d680, {0x55cb216c9c50, 0x55cb218f5f00}, 0x1d2ed3b243c0)
    	/build/go-task/src/go-task/task.go:142 +0x1a8
    github.com/go-task/task/v3.(*Executor).runDeps.func1()
    	/build/go-task/src/go-task/task.go:329 +0x9a
    golang.org/x/sync/errgroup.(*Group).Go.func1()
    	/build/go-task/src/pkg/mod/golang.org/x/sync@v0.19.0/errgroup/errgroup.go:93 +0x50
    created by golang.org/x/sync/errgroup.(*Group).Go in goroutine 1
    	/build/go-task/src/pkg/mod/golang.org/x/sync@v0.19.0/errgroup/errgroup.go:78 +0x95
    

    As a workaround, it seems like you can hard-code it or do something lovecraftian like this:

     sources:
        - "{{.FILE | replace \"'\" \"\\\\'\"}}"
    
  13. trulede commented on Mar 24, 2026

    @trulede
    Contributor

    Template function quote or shellQuote might also help. We would have to look at mvdan to understand exactly what is going on, the shell obviously expects something, so it needs escaping.

    This:

    vars:
            FILES:
              sh: find . -name '*.txt' -printf "%f\n"
    

    is evaluated before the task runs, so the dir the command runs in may not be what you expect. There is a issue open about that ... its not so easy to fix.

    There are issues for all of these things already. And PR's too in some cases.

  14. added and removed
    state: needs triageWaiting to be triaged by a maintainer.
    on Mar 26, 2026
  15. trulede commented on Apr 15, 2026

    @trulede
    Contributor

    @loganfred Did you try quote or shellQuote? Quoting the file name correctly seems to be the root cause of your problem.

    I'm not sure if task should quote/escape this automatically, perhaps your AI bot can find the place in the code that does escaping and write a PR there? There is a place ....

  16. self-assigned this
    on Apr 15, 2026
  17. assigned and unassigned on Apr 19, 2026
  18. trulede commented on Apr 25, 2026

    @trulede
    Contributor

    @loganfred I may have also found a solution to the panic.

    #2810

  19. loganfred commented on Apr 25, 2026

    @loganfred
    Author

    @loganfred Did you try quote or shellQuote? Quoting the file name correctly seems to be the root cause of your problem.

    I'm not sure if task should quote/escape this automatically, perhaps your AI bot can find the place in the code that does escaping and write a PR there? There is a place ....

    Yes, the fixing the shell quoting solved the panic, but I'm glad #2810 will handle it.

    It seems like the other footguns I stumbled into are covered by other issues, so I'll update the title to be clearer and close this one.

    Thanks for the assistance, I think the project is neat.

  20. changed the title [-]SIGSEGV in fingerprint; explicit dir include dynamic vars are not isolated and caching prohibits exact shell command re-use[/-] [+]SIGSEGV / panic in fingerprint from apostrophe in env var shell command[/+] on Apr 25, 2026
  21. changed the title [-]SIGSEGV / panic in fingerprint from apostrophe in env var shell command[/-] [+]SIGSEGV / panic in fingerprint from apostrophe in env var shell command filename[/+] on Apr 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions