Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

sana and scat: crashes, and records lost or doubled with exit 0

Open
#617 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
25/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
go

Research direction

The report covers sana and scat parsing, file discovery, process watching, and live streaming, but names no source files or tests. Start by locating both command entry points and the relevant tests, then trace the reported cases one at a time. Done means each expected behavior is fixed and covered, including errors, without losing or duplicating records.

Written by the indexing model from the issue text.

Description

Version: seqkit v2.14.0 (bioconda), Linux x86_64

All examples below use small files made with printf. Each problem is a crash,
a lost record or a doubled record. In most cases the command exits 0.

Test files used in more than one section:

$ printf '@a\nACGT\n+\nIIII\n' > good.fq
$ mkdir t7 && cp good.fq t7/a.fq

sana: -i '' stops with a Go deadlock

What happens

Every other bad value of -i gives [CRIT] Invalid format specified. An empty
value gives a Go deadlock and a goroutine trace.

$ seqkit sana -i '' good.fq
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send (nil chan)]:
...
$ echo $?
2
Expected

The same [CRIT] Invalid format specified message as for other bad values.

sana: a directory is read as an empty file (exit 0)

$ mkdir d
$ seqkit sana d
[INFO] File: d	Pass records: 0	Discarded lines: 0
$ echo $?
0
Expected

An error such as read d: is a directory and a non-zero exit code.

sana -i fasta: one bad line before the first header discards the whole file

What happens

A line such as junk or ;comment before the first header discards every
record of the file, good records included. FASTQ input drops only the bad line.
The line numbers in the messages are also wrong (-1, 0).

$ printf 'junk\n>a\nACGT\n>b\nGGCC\n' > junk.fa
$ seqkit sana -i fasta junk.fa
[INFO] File: junk.fa	Discarded line: line 1: expected header line, got: junk	-1:	 junk
[INFO] File: junk.fa	Discarded line: line 1: expected header line, got: junk	0:	 >a
[INFO] File: junk.fa	Discarded line: line 1: expected header line, got: junk	1:	 ACGT
[INFO] File: junk.fa	Discarded line: line 1: expected header line, got: junk	2:	 >b
[INFO] File: junk.fa	Pass records: 0	Discarded lines: 4
$ echo $?
0
Expected

Only junk is discarded. Records a and b pass.

sana -i fasta: a line > alone discards the record before it

What happens

A header line with an empty ID is read as a sequence line of the record before
it. That good record then fails. FASTQ accepts the same empty ID.

$ printf '>a\nACGT\n>\n>b\nGGCC\n' > gt.fa
$ seqkit sana -i fasta gt.fa
[INFO] File: gt.fa	Discarded line: line 3: invalid sequence line: >	-1:	 >a
[INFO] File: gt.fa	Discarded line: line 3: invalid sequence line: >	0:	 ACGT
[INFO] File: gt.fa	Discarded line: line 3: invalid sequence line: >	1:	 >
[INFO] File: gt.fa	Pass records: 1	Discarded lines: 3
>b
GGCC
Expected

Record a passes. The > line is a header with an empty ID.

sana -i fasta: a file with CRLF line ends loses every record (exit 0)

What happens

The CR is taken as a bad sequence letter. FASTQ input removes the CR.

$ printf '>a\r\nACGT\r\n>b\r\nGGCC\r\n' > crlf.fa
$ seqkit sana -i fasta crlf.fa
[INFO] File: crlf.fa	Discarded line: line 2: invalid sequence line: ACGT	-1:	 >a
[INFO] File: crlf.fa	Discarded line: line 2: invalid sequence line: ACGT	0:	 ACGT
[INFO] File: crlf.fa	Discarded line: line 4: invalid sequence line: GGCC	1:	 >b
[INFO] File: crlf.fa	Pass records: 0	Discarded lines: 3
$ echo $?
0
Expected

The CR at each line end is removed, as for FASTQ. Both records pass.

sana -i fasta: a failed last record is not fully reported

What happens

When the LAST record fails, the sequence lines after the bad line are not
listed and not counted. The same record in the middle of the file is listed
completely.

$ printf '>s1\nAC\nGT\n>s2\nGG\nG1G\nTT\n' > badlast.fa
$ seqkit sana -i fasta badlast.fa 2>&1 >/dev/null
[INFO] File: badlast.fa	Discarded line: line 6: invalid sequence line: G1G	2:	 >s2
[INFO] File: badlast.fa	Discarded line: line 6: invalid sequence line: G1G	3:	 GG
[INFO] File: badlast.fa	Discarded line: line 6: invalid sequence line: G1G	4:	 G1G
[INFO] File: badlast.fa	Pass records: 1	Discarded lines: 3

$ printf '>s1\nAC\nGT\n>s2\nGG\nG1G\nTT\n>s3\nAA\n' > badmid.fa
$ seqkit sana -i fasta badmid.fa 2>&1 >/dev/null
...	4:	 G1G
...	5:	 TT
[INFO] File: badmid.fa	Pass records: 2	Discarded lines: 4
Expected

TT is listed, and Discarded lines: 4, for the last record too.

scat -f: one dangling symlink stops the run, and no record comes out

$ mkdir t6 && printf '@a\nACGT\n+\nIIII\n' > t6/a.fastq && ln -s nowhere.fq t6/dangling.fq
$ seqkit scat -f --quiet t6
[ERRO] stat t6/dangling.fq: no such file or directory
$ echo $?
255

Record a is lost. With more files, some records come out or none, by chance.

Expected

An error for the bad link, and all other files are read.

scat -f: a file that cannot be read is skipped with no message (exit 0)

$ mkdir t2 && printf '@a\nACGT\n+\nIIII\n' > t2/a.fq && printf '@u\nACGT\n+\nIIII\n' > t2/u.fq && chmod 000 t2/u.fq
$ seqkit scat -f --quiet t2
@a
ACGT
+
IIII
$ echo $?
0
Expected

open t2/u.fq: permission denied and a non-zero exit code.

scat -i fasta: the same three FASTA problems as sana

A line before the first header, CRLF line ends and a last line > each lose
the good records of the file, with exit 0.

$ mkdir t3 && printf 'junk\n>a\nACGT\n' > t3/a.fa
$ seqkit scat -f --quiet -i fasta t3          # no record
$ mkdir t4 && printf '>a\r\nACGT\r\n' > t4/a.fa
$ seqkit scat -f --quiet -i fasta t4          # no record
$ mkdir t5 && printf '>a\nACGT\n>\n' > t5/a.fa
$ seqkit scat -f --quiet -i fasta t5          # no record
From file: t5/a.fa	Discarded line: line 3: invalid sequence line: >	-1:	 >a
From file: t5/a.fa	Discarded line: line 3: invalid sequence line: >	0:	 ACGT
Expected

Record a comes out in all three cases.

scat -p: works the wrong way round

What happens

The help says "after process with this PID exited". With the PID of a running
process of the same user, scat ends after about 0.5 s and says the process
exited. With a PID that does not exist, scat never ends.

$ seqkit scat -p $$ t7
[INFO] Running until process with PID 3212779 exits.
[INFO] Watched process with PID 3212779 exited.
...
$ kill -0 $$ && echo alive
alive

$ timeout 5 seqkit scat --quiet -p 999999 t7; echo $?
...
124
Expected

scat runs while the process exists, and ends when it no longer exists.

scat (live mode): renaming a watched file crashes

$ mkdir L4 && printf '@a\nACGT\n+\nIIII\n' > L4/n.fq
$ seqkit scat L4 &           # then, 2 s later:
$ mv L4/n.fq L4/m.fq
[INFO] Stopped watching renamed file: L4/n.fq
[CRIT] Invalid renaming feedback: 2

Exit code 1. In other runs the records of the renamed file come out a second
time instead.

Expected

No crash, and each record comes out once.

scat (live mode): a small append does not come out until Ctrl-C

What happens

The help says "real time ... streaming". With -d 0, one record appended to a
watched file does not come out. It comes out only when scat gets SIGINT.
A large append (about 66 KB) comes out within a few seconds.

$ mkdir L3 && printf '@a\nACGT\n+\nIIII\n' > L3/n.fq
$ seqkit scat -d 0 L3 > out.fq &      # then:
$ printf '@b\nGGCC\n+\nIIII\n' >> L3/n.fq
$ grep -c '^@' out.fq                 # 5 s later
1
                                      # after SIGINT: 2
Expected

The appended record comes out without waiting for the end.

scat: -X/--infile-list is ignored

$ printf 't7\n' > list.txt; mkdir t8
$ seqkit scat -f -X list.txt t8
...
[INFO] Streaming fastq records from directory: t8
[INFO] Total stats:	Pass records: 0	Discarded lines: 0
Expected

The folder t7 from the list is read, or an error says that -X is not
supported.

scat: the same folder twice, or a folder and its sub-folder, doubles records

$ seqkit scat -f --quiet t7 t7          # record a twice
$ mkdir -p t9/sub && printf '@s\nACGT\n+\nIIII\n' > t9/sub/s.fq
$ seqkit scat -f --quiet t9 t9/sub      # record s twice

Exit code 0 in both cases.

Expected

Each file is read once.

Dominant language
Go
Stars
1.6k
Forks
189
PR merge metrics
No merged PRs in 30d

Getting set up

  • Ships a Dockerfile or Docker Compose file
  • No pull request template
  • No contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from shenwei356/seqkit

All issues in shenwei356/seqkit

Similar issues

More Go issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.