James Fellows Yates
02/04/2026, 3:07 PMbowtie2/align module replacing the context of the reference FASTA file with a SAM file 😱
We've identified the problem being that the find command used to pick up the index directory was finding two sets of *.bt2 index files, and unfortunately the prefix of the second set was the same name as the reference FASTA itself. Meaning this:
bowtie2 \\
-x \$INDEX \\
-1 sample.fastq
ended up resolving to
bowtie2 \\
-x ./hs37d5/bowtie2 ./hs37d5/hs37d5.fa \\
-1 sample.fastq
With bowtie2 assuming the first non-argumented file was where the output should go into...
So I'm wondering what the next course of action should be... (improve the find pattern? Add a validation check and fail if n > 1 index files)
TL;DR has anyone else come across a similar case of an aligner module overwriting an index, and already addressed this with an updated find command I can copy, and/or have a suggestion of a preferred way to counter this?Evangelos Karatzas
02/04/2026, 3:17 PMfind here?James Fellows Yates
02/04/2026, 3:19 PMEvangelos Karatzas
02/04/2026, 3:21 PMAdam Talbot
02/04/2026, 3:21 PMThe problem is bowtie2 (and stuff like bwa) make multiple reference files.It might be easier to provide a tuple input:
tuple val(name), path(bwt), path(fa), path(bt2) which is explicit rather than implicit.
Sounds like a lot of work. BYEeEEEE 🏃Adam Talbot
02/04/2026, 3:21 PMJames Fellows Yates
02/04/2026, 3:22 PMJames Fellows Yates
02/04/2026, 3:22 PMJames Fellows Yates
02/04/2026, 3:22 PMAdam Talbot
02/04/2026, 3:22 PMBwtIndex record as an input:
input:
val id
path forward_fastq
path reverse_fastq
BwtIndex bwt_indexJames Fellows Yates
02/04/2026, 3:23 PMbwt_index get picked up up though is the issueJames Fellows Yates
02/04/2026, 3:23 PMAdam Talbot
02/04/2026, 3:23 PMJames Fellows Yates
02/04/2026, 3:23 PMAdam Talbot
02/04/2026, 3:23 PMAdam Talbot
02/04/2026, 3:25 PM// Detect BWA index files (auto-discover based on FASTA path)
bwa_pattern = bwa ?: fasta + ".{bwt,pac,ann,amb,sa}"
bwa_files = files(bwa_pattern, checkIfExists: true)
ch_bwa = Channel.of(bwa_files)
.map { bwa_file_list -> validateBwaIndexFiles(bwa_file_list) }
.map { bwa_file_list -> tuple([id: "bwa"], bwa_file_list) }Adam Talbot
02/04/2026, 3:26 PMmap { bwa_file_list -> createBwtIndex(bwa_file_list) }James Fellows Yates
02/04/2026, 3:28 PMJames Fellows Yates
02/04/2026, 3:28 PMAdam Talbot
02/04/2026, 3:31 PMbwa_pattern = params.bowtie2_index + ".{bwt,pac,ann,amb,sa}"
It's just constructing a string which you pass to file()
Going back to the original problem. How did two bt2 index files end up in the process? Can we fix that and be more specific about which files are in that index input?James Fellows Yates
02/04/2026, 3:31 PMJames Fellows Yates
02/04/2026, 3:31 PMfindJames Fellows Yates
02/04/2026, 3:32 PMINDEX=`find -L ./ -name "*.rev.1.bt2" | sed "s/\\.rev.1.bt2\$//"`
[ -z "\$INDEX" ] && INDEX=`find -L ./ -name "*.rev.1.bt2l" | sed "s/\\.rev.1.bt2l\$//"`
[ -z "\$INDEX" ] && echo "Bowtie2 index files not found" 1>&2 && exit 1James Fellows Yates
02/04/2026, 3:32 PMJames Fellows Yates
02/04/2026, 3:32 PMAdam Talbot
02/04/2026, 3:32 PMAdd a validation check and fail if n > 1 index filesIn Nextflow?
Adam Talbot
02/04/2026, 3:32 PMJames Fellows Yates
02/04/2026, 3:33 PMnextflow discover the files already? Or do we rely on the existing find patternJames Fellows Yates
02/04/2026, 3:33 PMAdam Talbot
02/04/2026, 3:34 PMbowtie_index/*?James Fellows Yates
02/04/2026, 3:35 PMJames Fellows Yates
02/04/2026, 3:35 PMJames Fellows Yates
02/04/2026, 3:35 PMAdam Talbot
02/04/2026, 3:35 PMdef bowtie_index = files.anyOf { groovy goes here } ?James Fellows Yates
02/04/2026, 3:36 PMJames Fellows Yates
02/04/2026, 3:39 PMJames Fellows Yates
02/04/2026, 3:40 PMJames Fellows Yates
02/04/2026, 3:40 PMAdam Talbot
02/04/2026, 3:45 PMtest.1.bt2 test.2.bt2 test.3.bt2 test.4.bt2 test.rev.1.bt2 test.rev.2.bt2
The name of the index is completely arbitrary when you run bowtie2-build, in ☝️ case it's test
In the find , we detect the .rev.1.bt2 , then remove that suffix. If that fails, we find the .rev.1.bt2l. If that fails we error.
When people are using the BOWTIE2_ALIGN module are they generally supplying a directory or list of files?James Fellows Yates
02/04/2026, 3:46 PMJames Fellows Yates
02/04/2026, 3:46 PMJames Fellows Yates
02/04/2026, 3:46 PMAdam Talbot
02/04/2026, 3:46 PMAdam Talbot
02/04/2026, 3:48 PMscript:
// Handle both directory and file collection inputs
def index_files = index instanceof List ? index : file(index).listFiles()?.toList() ?: [index]
def idx_file = index_files.find { it.name.endsWith('.rev.1.bt2') || it.name.endsWith('.rev.1.bt2l') }
if (!idx_file) error "Bowtie2 index files not found in: ${index}"
def index_base = idx_file.name.replaceFirst(/\.rev\.1\.bt2l?$/, '')
// If input was a directory, prepend the directory name
def index_path = index instanceof List ? index_base : "${index.name}/${index_base}"
def args = task.ext.args ?: ''
def prefix = task.ext.prefix ?: "${meta.id}"
"""
bowtie2 \\
-x ${index_path} \\
-p ${task.cpus} \\
${args} \\
...
"""Adam Talbot
02/04/2026, 3:50 PM${index_path} , because it would only ever use a single value.Adam Talbot
02/04/2026, 3:51 PMJames Fellows Yates
02/04/2026, 3:51 PMJames Fellows Yates
02/04/2026, 3:52 PMAdam Talbot
02/04/2026, 3:52 PM./hs37d5/hs37d5.rev.1.bt2
./hs37d5/hs37d5.fa.rev.1.bt2 // Nextflow might pick this one!James Fellows Yates
02/04/2026, 3:52 PMAdam Talbot
02/04/2026, 3:52 PMJames Fellows Yates
02/04/2026, 3:52 PMidx_file definition have a check that the result should be <= 1James Fellows Yates
02/04/2026, 3:52 PMJames Fellows Yates
02/04/2026, 3:53 PMfind{} -> findAll{} (if that's a thing?)Adam Talbot
02/04/2026, 3:53 PMscript:
def index_files = index instanceof List ? index : file(index).listFiles()?.toList() ?: [index]
// Find ALL potential index bases, not just the first
def idx_matches = index_files.findAll { it.name.endsWith('.rev.1.bt2') || it.name.endsWith('.rev.1.bt2l') }
if (idx_matches.size() == 0) {
error "Bowtie2 index files not found in: ${index}"
}
if (idx_matches.size() > 1) {
error "Multiple Bowtie2 index sets found: ${idx_matches.collect { it.name.replaceFirst(/\.rev\.1\.bt2l?$/, '') }.join(', ')}. Please provide only one index."
}
def index_base = idx_matches[0].name.replaceFirst(/\.rev\.1\.bt2l?$/, '')
def index_path = index instanceof List ? index_base : "${index.name}/${index_base}"
// ... rest of scriptJames Fellows Yates
02/04/2026, 3:53 PMJames Fellows Yates
02/04/2026, 3:53 PMJames Fellows Yates
02/04/2026, 3:54 PMfind 😅Adam Talbot
02/04/2026, 3:54 PMAdam Talbot
02/04/2026, 3:54 PMJames Fellows Yates
02/04/2026, 3:55 PMJames Fellows Yates
02/04/2026, 3:55 PMJames Fellows Yates
02/04/2026, 3:55 PMJames Fellows Yates
02/04/2026, 3:56 PMAdam Talbot
02/04/2026, 4:00 PMoutput:
tuple val(meta), val(index_base), path("${index_base}*.bt2{,l}"), emit: index
Which can be directly plumbed into `BOWTIE2_ALIGN`:
input:
tuple val(meta), path(reads)
tuple val(meta2), val(index_base), path(index_files)
script:
"""
bowtie2 \\
-x ${index_base} \\Evangelos Karatzas
02/04/2026, 4:27 PMJames Fellows Yates
02/04/2026, 4:31 PMEvangelos Karatzas
02/04/2026, 4:34 PMBOWTIE2_INDEX does not have to run. But then the downstream module would definitely need the extra checking step to make sure it's the proper format. I see your pointAdam Talbot
02/04/2026, 5:04 PMmap )Jim Downie
02/04/2026, 6:11 PMSimon Pearce
02/04/2026, 7:36 PMMatthieu Muffato
02/04/2026, 9:54 PMlistFiles() doesn’t work on symlinks. Need to call toRealPath() first to resolve the symlink.Adam Talbot
02/05/2026, 10:18 AMsymlink.listFiles() -> [ file1, file2, ... ]
realfile.listFiles() -> [ symlink1, symlink2, ...]Adam Talbot
02/05/2026, 10:21 AM.toRealPath() won't work on remote filesystemsMatthieu Muffato
02/05/2026, 10:27 AMlistFiles() doesn’t work when used on the symbolic link itselfAdam Talbot
02/05/2026, 10:28 AMAdam Talbot
02/05/2026, 10:31 AMJim Downie
02/05/2026, 10:42 AMAdam Talbot
02/05/2026, 10:50 AMlistFiles0()) so it should help both listFiles and listDirectory 🤞James Fellows Yates
02/05/2026, 2:13 PMJames Fellows Yates
02/05/2026, 2:13 PMAdam Talbot
02/05/2026, 3:35 PMJames Fellows Yates
02/05/2026, 4:37 PMAdam Talbot
02/10/2026, 5:04 PMlistDirectory() in the near future: https://github.com/nextflow-io/nextflow/pull/6821#event-22676287462