# How to Index a FASTA File: Using samtools faidx

You will be able to build a `.fai` index for a plain or bgzipped FASTA file, fetch any region by name and coordinates, confirm that the index matches the sequence, and recognize the errors that stop indexing. You need a FASTA file, a terminal, and samtools 1.x. For compressed input you also need bgzip, which ships with htslib and is usually installed alongside samtools.

The command is short. The details around it are where most problems live. A stale index, a Windows line ending, a duplicated sequence name, or a plain gzip file can produce wrong sequence or a failed run without an obvious warning. This guide walks through the whole workflow, including the index format itself, so you can check the result rather than assume it.

## What a FASTA Index Actually Is

FASTA is a text format for biological sequences. Each record starts with a header line that begins with `>`. The first whitespace-delimited word after `>` is the sequence name. The following lines contain the sequence, usually wrapped at a fixed width. A reference genome FASTA might contain chromosomes, scaffolds, or contigs. A protein FASTA contains amino acid sequences. A transcript FASTA contains spliced or unspliced RNA sequences.

An index for a FASTA file is a small companion file that records where each sequence starts in the byte stream. The standard index created by `samtools faidx` is a plain-text file named after the FASTA with `.fai` appended. For `reference.fa`, the index is `reference.fa.fai`. It does not contain sequence. It contains coordinates.

The practical value is random access. Without an index, a tool that needs `chr7:117,199,000-117,200,000` must scan from the beginning of the file until it reaches chromosome 7, then scan again to the requested position. With an index, the tool reads one line from the `.fai`, computes a byte offset, seeks directly to that offset, and reads only the requested bases. The same principle appears throughout bioinformatics. STABIX, for example, was built because finding all SNVs in a p-value range in a standard bgzip and Tabix file would otherwise require decompressing and scanning the whole file [1]. BigWig and BigBed files use indexes so that only the data needed for the current browser view is transmitted rather than the entire file [2]. A portable JavaScript VCF reader uses byte-range requests and binary search to fetch partial content without downloading the whole file [3]. The `.fai` is the FASTA version of that idea.

An index also lets many tools start work without reading the entire reference. Aligners may have their own index formats, but variant callers, visualization tools, and region query utilities often expect a `.fai`. If the file is missing, some tools build it automatically. Others fail with a message about a missing index. Building it once, explicitly, avoids surprises.

The `.fai` is not a validation certificate. It does not checksum the sequence, record the assembly name, or prove that the bases are correct. It only describes the layout of the text file. If the FASTA changes and the `.fai` does not, the index becomes a map to the wrong bytes.

## FASTA vs FASTQ and Other Indexed Formats

FASTA and FASTQ are both text formats for sequences. They serve different purposes. FASTA stores sequence only. FASTQ stores sequence plus a per-base quality score. A FASTQ record has four lines: a header beginning with `@`, the sequence, a separator beginning with `+`, and a quality string of the same length as the sequence. The quality string encodes Phred scores. FASTQ is the usual output of a sequencing instrument. FASTA is the usual format for a reference genome, a transcript set, or a protein set.

Quality scores are the difficult part of FASTQ compression and random access. LCQS notes that quality scores are hard to compress and that random access requires a fine-grained index structure, with tradeoffs in compression speed and index size [4]. SCALCE notes that FASTQ data have a limited alphabet and high similarity among reads, which general-purpose gzip does not exploit [5]. Those concerns do not apply to a reference FASTA in the same way. A reference FASTA is a small number of long sequences with a four-letter alphabet and no quality values. Its index is correspondingly simple.

Common bioinformatics file formats use different index types:

| Format | Content | Typical index | Created by |
|--|--|--|--|
| FASTA | nucleotide or [protein sequence](/blog/guides/protein-sequence) | `.fai` | `samtools faidx` |
| FASTQ | reads plus quality scores | no universal index | tool-specific |
| BAM | aligned reads | `.bai` or `.csi` | `samtools index` |
| CRAM | compressed aligned reads | `.crai` | `samtools index` |
| VCF | variants | `.tbi` or `.csi` | `tabix` or `bcftools index` |
| BED, GFF, GTF | intervals and annotations | `.tbi` or `.csi` | `tabix` |
| BigWig, BigBed | signal or intervals | internal binary index | UCSC utilities |

The table matters because the word "index" is overloaded. `samtools faidx` does not create a BAM index, a variant index, or an aligner index. `samtools index` does not index a FASTA. `bwa index` creates a set of files for alignment, not for region retrieval. `tabix` indexes bgzipped tab-delimited files, not FASTA. Mixing these up is a common source of wasted time.

The scale of FASTA data is another reason indexes matter. A collection of 1,092 human diploid genomes is about 6.7 TB as uncompressed FASTA, which is why specialized compression tools exist [6]. Most working reference files are far smaller, but a human reference still has about 3.1 billion bases. Scanning it repeatedly for every region query is wasteful. The `.fai` turns those queries into seeks.

## Before You Start

You need three things:

1. A FASTA file with a `.fa`, `.fasta`, `.fna`, or similar extension. The extension does not affect indexing.
2. A working samtools installation, version 1.x. Check it before building anything.
3. Write permission in the directory that contains the FASTA, because the index is written next to the input by default.

Check the version:

```bash
samtools --version
```

Expected output looks like this, though your exact version may differ:

```text
samtools 1.19
Using htslib 1.19
Copyright (C) 1993-2024 [Genome Research](/blog/guides/genome-research) Ltd.
```

If the command is not found, install samtools from your package manager or from Bioconda. On a cluster, load the module first. The commands in this guide use the standard `samtools faidx` interface that has been stable for years.

Check the file before indexing:

```bash
ls -lh reference.fa
head -n 4 reference.fa
grep -c '^>' reference.fa
```

Expected output for a small reference might look like this:

```text
-rw-r--r--  1 user  group  3.1G  Nov 10 09:14 reference.fa
>chr1
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
25
```

The `grep -c '^>'` count is the number of records. It should match the number of data rows in the `.fai` after indexing, unless the FASTA contains duplicate names or empty records.

If the FASTA is compressed, decide now whether it is BGZF-compressed. A plain `gzip` file is not suitable for `samtools faidx`. BGZF is the block gzip format used by BAM, VCF, and `bgzip`. It supports random access because the file is divided into independently decompressible blocks. A plain gzip file is a single continuous stream and cannot be seeked into efficiently.

## Index a FASTA File with samtools faidx: Step by Step

This numbered walkthrough is the core workflow. Run the commands from the directory that contains your FASTA, or adjust the paths.

### Step 1: Inspect the FASTA for obvious layout problems

A FASTA can be valid text and still be a poor indexing candidate. Check that every record has sequence data, that names are unique, and that sequence lines within a record are not wildly inconsistent.

```bash
awk '/^>/{if (name != "") print name, length(seq); name=$1; seq=""; next}{seq=seq$0}END{if (name != "") print name, length(seq)}' reference.fa | head
```

Expected output:

```text
>chr1 248956422
>chr2 242193529
>chr3 198295559
```

This command loads one sequence at a time into memory. It is fine for a quick check on a small file. For a 3 GB human reference, it will use several gigabytes of RAM because it concatenates each chromosome. A lighter check is to look at the first few records with `head` and to count headers with `grep`.

Check for duplicate names:

```bash
grep '^>' reference.fa | awk '{print $1}' | sort | uniq -d | head
```

No output means no duplicates. If you see names, fix them before indexing.

### Step 2: Verify samtools is available

```bash
samtools --version
```

You need a 1.x release. The exact patch version rarely matters for basic FASTA indexing. If you use a very old release, upgrade. The `.fai` format is stable, but error messages and BGZF support have improved.

### Step 3: Compress with bgzip if you want a compressed FASTA

Skip this step for a plain FASTA. If you want compressed storage and random access, use `bgzip`, not `gzip`.

```bash
bgzip -k reference.fa
ls -lh reference.fa.gz
```

The `-k` flag keeps the original file. The result is `reference.fa.gz` in BGZF format. To use multiple threads:

```bash
bgzip -@ 4 -k reference.fa
```

Do not do this:

```bash
gzip -c reference.fa > reference.fa.gz
```

That produces a plain gzip file. `samtools faidx` may fail with a BGZF-related error when it tries to read it. If you already have a plain gzip file, decompress it and recompress with `bgzip`.

```bash
gzip -dc reference.fa.gz > reference.fa
bgzip -k reference.fa
```

### Step 4: Build the index

For a plain FASTA:

```bash
samtools faidx reference.fa
```

For a bgzipped FASTA:

```bash
samtools faidx reference.fa.gz
```

A successful run prints nothing. That silence is normal. Check the files:

```bash
ls -lh reference.fa.fai
```

For the compressed case:

```bash
ls -lh reference.fa.gz.fai reference.fa.gz.gzi
```

Expected output:

```text
-rw-r--r-- 1 user group 1.2K Nov 10 09:20 reference.fa.gz.fai
-rw-r--r-- 1 user group 8.4M Nov 10 09:20 reference.fa.gz.gzi
```

The `.fai` is small. The `.gzi` is larger because it maps compressed blocks to uncompressed offsets. Both are needed for random access to a bgzipped FASTA.

### Step 5: Inspect the .fai

```bash
head -n 5 reference.fa.fai
```

Expected output:

```text
chr1	248956422	80	60	61
chr2	242193529	123456789	60	61
chr3	198295559	246913578	60	61
chr4	190214555	370209137	60	61
chr5	181538259	493728291	60	61
```

The columns are name, length, offset, bases per line, and bytes per line. The offset and line values depend on the header lengths and line wrapping in your file. Your numbers will differ.

### Step 6: Fetch a region

```bash
samtools faidx reference.fa chr1:1-60
```

Expected output:

```text
>chr1:1-60
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
```

The output header includes the region you requested. The sequence is wrapped at 60 bases per line by default. Coordinates are 1-based and inclusive. `chr1:1-60` returns 60 bases. `chr1:1-1` returns one base.

Fetch multiple regions in one command:

```bash
samtools faidx reference.fa chr1:1-60 chr2:1-60
```

Fetch a whole chromosome:

```bash
samtools faidx reference.fa chr1
```

### Step 7: Check the index against the file

Print the length recorded in the `.fai` for a sequence:

```bash
awk '$1=="chr1"{print $2}' reference.fa.fai
```

Expected output:

```text
248956422
```

Ask samtools for the same length directly:

```bash
samtools faidx -n reference.fa chr1
```

Expected output:

```text
248956422
```

The two numbers must match. If they do not, the index is stale or the FASTA layout changed.

Count records and index rows:

```bash
grep -c '^>' reference.fa
wc -l < reference.fa.fai
```

For a clean file, the counts match. Duplicate names or empty sequences can cause a mismatch.

### Step 8: Use the index in downstream tools

Many tools find the `.fai` automatically when it sits next to the FASTA. Examples:

```bash
samtools view -T reference.fa aligned.bam
samtools mpileup -f reference.fa aligned.bam
bcftools mpileup -f reference.fa aligned.bam
```

GATK and Picard-style tools often require both a `.fai` and a sequence dictionary (`.dict`). Create the dictionary with:

```bash
samtools dict -o reference.dict reference.fa
```

The `.fai` and `.dict` are different files. The `.fai` describes byte layout for random access. The `.dict` describes sequence names, lengths, assembly metadata, and checksums for tools that need a formal sequence dictionary.

## Reading the .fai Format: A Worked Example

The `.fai` has five tab-separated columns. Every column has a precise meaning.

| Column | Name | Meaning |
|--|--|--|
| 1 | NAME | Sequence name, the first word after `>` |
| 2 | LENGTH | Number of bases or residues in the sequence |
| 3 | OFFSET | Zero-based byte offset of the first base |
| 4 | LINEBASES | Number of bases per sequence line, excluding the newline |
| 5 | LINEWIDTH | Number of bytes per sequence line, including the newline |

Consider this small FASTA:

```text
>chr1
ACGTACGTAC
GTACGTACGT
AC
>chr2
TTTT
AAAA
```

The byte layout is:

- `>chr1\n` is 6 bytes, so the first base of `chr1` starts at byte offset 6.
- The first `chr1` sequence line is 10 bases plus a newline, so 11 bytes.
- The second line is also 11 bytes.
- The third line is 2 bases plus a newline, so 3 bytes.
- `chr1` length is 10 + 10 + 2 = 22.
- `>chr2\n` is 6 bytes, starting after 6 + 11 + 11 + 3 = 31 bytes, so the first base of `chr2` starts at byte offset 37.
- `chr2` has two lines of 4 bases, each 5 bytes including the newline.
- `chr2` length is 8.

The resulting `.fai` is:

```text
chr1	22	6	10	11
chr2	8	37	4	5
```

To fetch `chr1:5-12`, a tool computes the byte position of base 5:

```text
byte = offset + floor((start - 1) / LINEBASES) * LINEWIDTH + ((start - 1) % LINEBASES)
byte = 6 + floor(4 / 10) * 11 + (4 % 10)
byte = 6 + 0 + 4
byte = 10
```

Byte 10 is the fifth base of the first sequence line. The tool then reads across line breaks until it has 8 bases. The output is:

```text
>chr1:5-12
ACGTACGT
```

That formula is the reason inconsistent line lengths break an index. If one line in the middle of a sequence is shorter than the first line, the [offset calculation](/blog/data-analysis/excel-offset-function) lands on the wrong byte. The sequence after that point is shifted. The tool may return the wrong bases, or it may stop with a line length error.

The formula also explains why the last line can be shorter. The index records the line width from the full lines. The final line is handled by the length field. A short final line is normal. A short middle line is not.

## Key samtools faidx Options

Most users only need the bare `samtools faidx reference.fa` command. The same subcommand also fetches regions and has options that help with batch work.

| Option | What it does | Example |
|--|--|--|
| no region | Builds or rebuilds the `.fai` | `samtools faidx reference.fa` |
| `region` | Fetches a 1-based inclusive region | `samtools faidx reference.fa chr1:100-200` |
| `-o FILE` | Writes sequence output to a file | `samtools faidx -o out.fa reference.fa chr1:1-60` |
| `-n` | Prints the sequence length instead of the sequence | `samtools faidx -n reference.fa chr1` |
| `-r FILE` | Reads regions from a file, one per line | `samtools faidx -r regions.txt reference.fa` |
| `-c` | Continues after a region fails | `samtools faidx -c reference.fa chr1 chr99` |
| `-i` | Reverse-complements the output | `samtools faidx -i reference.fa chr1:1-60` |
| `-m TYPE` | Adds a strand marker to output headers | `samtools faidx -m sign reference.fa chr1:1-60` |

A region file is useful for batch extraction. Each line contains one region in the same syntax you would use on the command line.

```text
chr1:1-60
chr2:1000-1060
chrX:155000000-155000060
```

Run:

```bash
samtools faidx -r regions.txt reference.fa
```

If one region is missing, the default behavior is to stop. The `-c` option lets the command continue and report the failure for that region. Use it when you are extracting many regions and want the successful ones.

The `-i` option is handy when you need the reverse complement without writing a separate script. The output header still identifies the region. The `-m` option controls how the strand is marked in the header. Check the SAMtools manual page for the exact marker style in your version.

## Compressed FASTA: bgzip, .gzi, and Random Access

A compressed FASTA is common for reference archives and for distribution. The compression must be BGZF. BGZF is a series of gzip blocks with a virtual offset scheme. Each block can be decompressed independently. The `.gzi` index records the compressed and uncompressed offsets of those blocks. The `.fai` records the sequence layout in the uncompressed stream.

Workflows that use BGZIP and an index are standard across bioinformatics. GAF annotations, for example, are sorted, BGZIP-compressed, and indexed with HTSlib and tabix so that annotations overlapping a subgraph can be extracted quickly [7]. The same block compression model supports partial reads in web applications, where byte-range requests fetch only the needed chunks [3]. BigWig and BigBed take a related approach for browser display, transmitting only the data needed for the current view [2].

Build a compressed FASTA index in two commands:

```bash
bgzip -k reference.fa
samtools faidx reference.fa.gz
```

The output files are:

```text
reference.fa.gz
reference.fa.gz.fai
reference.fa.gz.gzi
```

Fetch a region directly from the compressed file:

```bash
samtools faidx reference.fa.gz chr1:1-60
```

Expected output:

```text
>chr1:1-60
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
```

Do not use `gzip -9` and expect random access. A plain gzip stream has no block boundaries that a reader can seek into. If you see an error such as `[E::bgzf_read_block] Invalid BGZF header at offset 0`, the file is probably plain gzip or another non-BGZF compression. Decompress and recompress with `bgzip`.

You can test whether a file is BGZF with the `file` command on many systems:

```bash
file reference.fa.gz
```

Expected output includes a phrase such as:

```text
reference.fa.gz: Blocked GNU Zip Format (BGZF; gzip compatible), block length 12345
```

The exact wording depends on your `file` version. The key phrase is BGZF.

Keep both index files together with the compressed FASTA. If you move the FASTA without the `.fai` and `.gzi`, region queries will fail or will rebuild a new index. If you copy only the `.fai`, compressed random access will not work because the block map is missing.

## Indexing Many FASTA Files Safely

Batch indexing is simple, but a few safeguards prevent half-indexed files and silent failures. Use a loop with strict error handling.

```bash
#!/usr/bin/env bash
set -euo pipefail

for f in "$@"; do
  if [[ ! -s "$f" ]]; then
    echo "missing or empty file: $f" >&2
    exit 1
  fi
  echo "indexing $f"
  samtools faidx "$f"
  echo "done $f"
done
```

Run it with a shell glob:

```bash
./index_fastas.sh *.fa *.fasta
```

The `set -euo pipefail` line stops the script on the first error. That is usually what you want. If one file has a line length problem, you want to know before a downstream pipeline starts.

For parallel indexing, use `xargs` with a modest number of processes:

```bash
printf '%s\n' *.fa | xargs -n 1 -P 4 samtools faidx
```

Four processes is a reasonable default on a laptop or small server. The index build is I/O-heavy, so more processes may not help. On a shared cluster, check the queue limits before using parallelism.

A simple loop without error handling is fine for a one-off:

```bash
for f in *.fa; do
  [ -e "$f" ] || continue
  samtools faidx "$f"
done
```

The `[ -e "$f" ] || continue` guard prevents the loop from trying to index a literal `*.fa` when no files match.

If you have both plain and compressed FASTA files, handle them by extension:

```bash
for f in *.fa *.fa.gz; do
  [ -e "$f" ] || continue
  case "$f" in
    *.gz) samtools faidx "$f" ;;
    *)    samtools faidx "$f" ;;
  esac
done
```

The same command works for both because `samtools faidx` detects BGZF input. The important difference is that compressed input also produces a `.gzi`.

## How to Check That the Index Is Correct

Do not stop at the absence of an error message. Run at least two checks.

First, fetch the first bases and compare them with the FASTA text.

```bash
samtools faidx reference.fa chr1:1-10
```

Expected output:

```text
>chr1:1-10
NNNNNNNNNN
```

Second, fetch the last base of a chromosome and confirm that it is a real base, not an empty record.

```bash
samtools faidx reference.fa chr1:248956422-248956422
```

Expected output:

```text
>chr1:248956422-248956422
N
```

Third, compare the length in the `.fai` with the length reported by `samtools faidx -n`.

```bash
awk '$1=="chr1"{print $2}' reference.fa.fai
samtools faidx -n reference.fa chr1
```

Both should print the same number.

Fourth, check for duplicate sequence names in the index.

```bash
cut -f1 reference.fa.fai | sort | uniq -d
```

No output means the names are unique.

Fifth, check that the index is newer than the FASTA.

```bash
if [ reference.fa.fai -nt reference.fa ]; then
  echo "index is newer than FASTA"
else
  echo "index may be stale"
fi
```

A newer index is not proof of correctness, but an older index is a red flag. If you edited the FASTA, rebuilt it, or replaced it with a different assembly, rebuild the index.

Sixth, compare record counts.

```bash
grep -c '^>' reference.fa
wc -l < reference.fa.fai
```

The counts should match for a clean file. If the `.fai` has fewer rows, some records may be empty. If it has more rows, something is wrong with the index or the count.

## Using the .fai with Downstream Tools

The `.fai` is a shared contract between many tools. A few common uses:

- `samtools view -T reference.fa` uses the FASTA and its index when reading CRAM or when a BAM header references sequences.
- `samtools mpileup -f reference.fa` needs the reference for pileup.
- `bcftools mpileup -f reference.fa` needs the reference for variant calling.
- `bedtools getfasta -fi reference.fa` can use the `.fai` for interval extraction.
- Genome browsers such as IGV use the `.fai` for region display.
- GATK requires a `.fai` and a `.dict` for reference-backed walkers.

The `.fai` also makes direct lookups fast in shell scripts. A small wrapper can extract a promoter, an exon, or a variant neighborhood without loading the whole chromosome.

```bash
samtools faidx reference.fa chr17:43044295-43125364
```

That is the BRCA1 region on a commonly used human reference. The exact coordinates depend on the assembly. Always confirm the assembly and chromosome naming before using hard-coded coordinates.

Chromosome naming is a frequent integration issue. Some references use `chr1`, others use `1`. Some use `chrM`, others use `MT`. The `.fai` records exactly what is in the FASTA. A BAM or VCF that uses a different naming convention will not match. Check the first column of the `.fai` against the sequence names in your other files.

```bash
head -n 3 reference.fa.fai | cut -f1
```

Expected output:

```text
chr1
chr2
chr3
```

If your alignments use `1`, `2`, `3`, you need a reference with matching names or a rename step. Do not rename chromosomes casually after alignment. Rebuild the affected files or use a conversion utility that updates all linked files.

## Common Mistakes and Limitations

### Inconsistent line lengths inside a sequence

This is the most common indexing failure. `samtools faidx` expects each sequence to have a consistent line width, with only the final line allowed to be shorter. A middle line that is shorter or longer breaks the offset calculation.

Typical error:

```text
[E::fai_build_core] Different line length in sequence 'chr1'.
```

Fix the wrapping so that every line except the last has the same length. A simple reformat with `awk` can rewrite the FASTA with a fixed width:

```bash
awk '/^>/{if (seq != "") print seq; print; seq=""; next}{seq=seq$0}END{if (seq != "") print seq}' reference.fa > reference.wrapped.fa
```

This command concatenates each record and prints it as one long line. That is valid FASTA and avoids line length variation entirely. For very long chromosomes, a single-line record is fine for indexing, though some downstream tools prefer wrapped lines. A fixed width of 60 or 80 is conventional.

### Windows CRLF line endings

A FASTA transferred from a Windows system may have `\r\n` line endings. The extra carriage return changes byte counts and can produce wrong offsets or a line length error. Convert to Unix line endings before indexing:

```bash
sed 's/\r$//' reference.fa > reference.unix.fa
mv reference.unix.fa reference.fa
```

Then rebuild the index.

### Blank lines and comment lines

Blank lines inside a record confuse the line width calculation. Comment lines beginning with `;` are allowed in some FASTA dialects but are not handled consistently by all tools. Remove blank lines and avoid comment lines in reference FASTA.

```bash
grep -v '^$' reference.fa > reference.clean.fa
```

### Duplicate sequence names

Duplicate names do not always stop the index build. They create duplicate rows in the `.fai`. A region query may return the first match, and downstream tools may silently use the wrong record. Check for duplicates before indexing.

```bash
grep '^>' reference.fa | awk '{print $1}' | sort | uniq -d
```

Rename duplicates or remove the redundant records.

### Spaces and descriptions in headers

The sequence name is the first word after `>`. A header such as `>chr1 Homo sapiens chromosome 1` is indexed as `chr1`. The description is not part of the name. This is normal FASTA behavior, but it surprises users who try to fetch the full header. Use the first token in region queries.

### Plain gzip instead of BGZF

A `.gz` extension does not guarantee BGZF. If the file was compressed with `gzip`, `samtools faidx` cannot build a usable random-access index. You may see a BGZF header error. Recompress with `bgzip`.

### A stale index after editing the FASTA

The `.fai` is not updated automatically when the FASTA changes. If you add a chromosome, trim a sequence, or replace the file with a new assembly, rebuild the index. When in doubt, delete the old index and rebuild.

```bash
rm -f reference.fa.fai reference.fa.gz.fai reference.fa.gz.gzi
samtools faidx reference.fa
```

### Empty sequences

A header followed immediately by another header creates an empty record. Some tools reject it. Some index it with length zero. Downstream region queries may fail. Replace empty records with a run of `N` bases if the sequence is truly unknown, or remove the record.

### Invalid or non-standard characters

`samtools faidx` is permissive about sequence characters because it treats the sequence as text. Downstream tools are less permissive. Nucleotide FASTA should use `A`, `C`, `G`, `T`, `N`, and IUPAC ambiguity codes. Protein FASTA uses the standard amino acid alphabet. A stray digit, `*`, or `-` may pass indexing and fail later. Check with a simple character count if you suspect a problem.

```bash
grep -v '^>' reference.fa | tr -d 'ACGTNacgtn' | head
```

No output means the file contains only the expected nucleotide characters. For a protein FASTA, adjust the character set.

### Index file location and permissions

The index is written next to the FASTA by default. If the directory is read-only, the build fails even if the FASTA is readable. Copy the FASTA to a writable directory or build the index elsewhere and move both files together.

Typical error:

```text
[E::fai_build3_core] Failed to open the file 'reference.fa.fai'
```

Check directory permissions and free space.

### Concurrency and race conditions

Two processes building the same index at the same time can produce a truncated or inconsistent `.fai`. Build indexes once, before launching parallel jobs. If a pipeline starts many tasks that each call `samtools faidx` on the same reference, pre-build the index and make the tasks read-only.

### Region not found

A typo in a chromosome name or a coordinate beyond the sequence length produces a warning and an empty result.

Typical messages:

```text
[W::fai_get_val] Reference 'chr99' not found in FASTA file, returning empty sequence
[faidx] Failed to fetch sequence in chr99:1-100
```

Check the first column of the `.fai` and the sequence length. Remember that coordinates are 1-based and inclusive. A request for `chr1:0-10` is invalid because position 0 does not exist.

### The .fai is not a validation certificate

The index says where the bytes are. It does not prove that the assembly is correct, that the sequence is complete, or that the file matches a published reference. If you need that level of confidence, use checksums from the data provider and a sequence dictionary with metadata.

### Limits of the format

A `.fai` supports retrieval by sequence name and coordinate. It does not support retrieval by sequence content, k-mer, variant, or annotation. For k-mer queries you need a k-mer index, and k-mer dictionaries can be memory-inefficient without one [8]. For read compression, tools such as minicom use k-minimizers to index reads and group similar sequences [9]. For genome collections, specialized compressors can outperform general-purpose tools, as shown by a collection of 1,092 human genomes compressed from about 6.7 TB of FASTA to about 700 MB [6]. Those are different problems from FASTA region access.

The `.fai` also does not compress the sequence. It is a text map beside a text file. If you need compression plus random access, use BGZF and the `.gzi`. If you need graph-based pangenome access, a linear `.fai` is not enough, and graph formats use their own indexes [7].

## Frequently Asked Questions

### What command creates a .fai index for a FASTA file?

Run `samtools faidx reference.fa`. It writes `reference.fa.fai` next to the input and prints nothing on success.

### Do I need a .fai index to read a FASTA file?

No for simple sequential reading. You need it for random access by region and for many tools that expect a `.fai` beside the reference.

### Can samtools faidx index a gzipped FASTA file?

Yes if the file is BGZF-compressed, which is what `bgzip` produces. A plain `gzip` file is not suitable because it lacks seekable blocks.

### What does "Different line length in sequence" mean?

It means one sequence has a short or long line in the middle of the record. Reformat the record so every line except the last has the same width, then rebuild the index.

### Where does samtools faidx write the index file?

It writes the index next to the input by default. For `reference.fa` the file is `reference.fa.fai`. For `reference.fa.gz` it writes `reference.fa.gz.fai` and `reference.fa.gz.gzi`.

### Is a .fai file the same as a .dict file?

No. A `.fai` stores byte offsets and line widths for random access. A `.dict` stores sequence names, lengths, and assembly metadata for tools such as GATK.

### How do I index many FASTA files at once?

Use a shell loop with `set -euo pipefail` and run `samtools faidx "$f"` for each file. For parallel work, use `xargs -n 1 -P 4 samtools faidx`.

### Does samtools faidx modify the FASTA file?

No. It reads the FASTA and writes a separate index file. The FASTA sequence and headers are unchanged.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "What command creates a .fai index for a FASTA file?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Run samtools faidx reference.fa. It writes reference.fa.fai next to the input and prints nothing on success."
      }
    },
    {
      "@type": "Question",
      "name": "Do I need a .fai index to read a FASTA file?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "No for simple sequential reading. You need it for random access by region and for many tools that expect a .fai beside the reference."
      }
    },
    {
      "@type": "Question",
      "name": "Can samtools faidx index a gzipped FASTA file?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Yes if the file is BGZF-compressed, which is what bgzip produces. A plain gzip file is not suitable because it lacks seekable blocks."
      }
    },
    {
      "@type": "Question",
      "name": "What does Different line length in sequence mean?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "It means one sequence has a short or long line in the middle of the record. Reformat the record so every line except the last has the same width, then rebuild the index."
      }
    },
    {
      "@type": "Question",
      "name": "Where does samtools faidx write the index file?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "It writes the index next to the input by default. For reference.fa the file is reference.fa.fai. For reference.fa.gz it writes reference.fa.gz.fai and reference.fa.gz.gzi."
      }
    },
    {
      "@type": "Question",
      "name": "Is a .fai file the same as a .dict file?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "No. A .fai stores byte offsets and line widths for random access. A .dict stores sequence names, lengths, and assembly metadata for tools such as GATK."
      }
    },
    {
      "@type": "Question",
      "name": "How do I index many FASTA files at once?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Use a shell loop with set -euo pipefail and run samtools faidx for each file. For parallel work, use xargs -n 1 -P 4 samtools faidx."
      }
    },
    {
      "@type": "Question",
      "name": "Does samtools faidx modify the FASTA file?",
      "acceptedAnswer": {
        "@type": "

## Sources

1. [STABIX: summary-statistic-based GWAS indexing and compression.](https://pubmed.ncbi.nlm.nih.gov/40315153/)
2. [BigWig and BigBed: enabling browsing of large distributed datasets.](https://pubmed.ncbi.nlm.nih.gov/20639541/)
3. [FAIR privacy-preserving operation of large genomic variant calling format (VCF) data without download or installation.](https://pubmed.ncbi.nlm.nih.gov/38827109/)
4. [LCQS: an efficient lossless compression tool of quality scores with random access functionality.](https://pubmed.ncbi.nlm.nih.gov/32183707/)
5. [SCALCE: boosting sequence compression algorithms using locally consistent encoding.](https://pubmed.ncbi.nlm.nih.gov/23047557/)
6. [GDC 2: Compression of large collections of genomes.](https://pubmed.ncbi.nlm.nih.gov/26108279/)
7. [Efficient indexing and querying of annotations in a pangenome graph.](https://pubmed.ncbi.nlm.nih.gov/39464141/)
8. [Indexing k-mers in linear space for quality value compression.](https://pubmed.ncbi.nlm.nih.gov/31856669/)
9. [Index suffix-prefix overlaps by (w, k)-minimizer to generate long contigs for reads compression.](https://pubmed.ncbi.nlm.nih.gov/30407482/)