Conversion · SAM → BAM
How to Convert SAM to BAM (Commands, Checks, and Pitfalls)
samtools view converts the bytes; sort and index are what make the BAM actually usable downstream.
By Ming "Tommy" Tang, Director of Bioinformatics in Big Pharma · Reviewed September 2026 · 3 min read
- SAM
- .sam · coordinates: 1-based-closed
- BAM
- .bam, .bai · coordinates: 1-based-closed
You need this conversion the moment an aligner (or a collaborator) hands you a SAM file. Everything past alignment, sorting, indexing, variant calling, loading into IGV, expects BAM, and most tools won't even open a plain SAM for region queries. Convert right after alignment; don't let SAM files pile up on disk as if they were a usable end state.
The conversion itself loses nothing. SAM and BAM encode the same records: same POS, CIGAR, FLAG, tags, same header. BAM is just that data BGZF-compressed into binary blocks, which is what makes it smaller and seekable. But "converted" isn't "usable", an unsorted BAM still fails on samtools index and on any tool that needs random access, so view, sort, and index are really one pipeline, not three optional steps.
The most common way this goes silently wrong isn't the conversion step, it's the sort step: writing the sorted output to the same filename as the input, e.g. samtools sort input.bam -o input.bam. The write starts truncating the file while samtools is still reading from it, and you end up with a corrupted BAM that some tools open fine and others choke on halfway through, a failure mode that surfaces two steps later, not at the moment you caused it.
The commands
Type your file names once; every command below updates.
01samtools
bashsamtools view -b sample.sam -o unsorted.bam-b tells samtools to write BAM instead of SAM; modern samtools autodetects SAM input from its content, so the older -S flag is optional and only kept for scripts targeting very old versions. Lossless: every field and the full header (@SQ contig names and lengths) carries through unchanged. The output here is deliberately unsorted, don't index or query it yet.
02samtools
bashsamtools sort -o sample.bam unsorted.bamCoordinate-sorts the BAM from the view step. This is a hard requirement before indexing; samtools index and most downstream tools reject or silently misbehave on anything else. Add -@ 4 for 4 threads and -m 2G to cap memory per thread once files get past a few GB, or the default sort spills a lot of temp files to disk. Use -T /path/prefix to point those temp files somewhere with room.
03samtools
bashsamtools index sample.bamBuilds a .bai index next to the sorted BAM, required by any tool that jumps to a specific region: IGV, samtools view chr1:1-1000, most variant callers. Only works on coordinate-sorted input, per the format spec, so an error here almost always means the sort step upstream didn't actually happen.
04samtools
bashsamtools flagstat sample.bamPrints total reads, mapped %, secondary/supplementary counts, and duplicates. Run this as your first check after any conversion, a mapped percentage that doesn't match what the aligner logged usually means you converted the wrong file or a truncated SAM.
05samtools
bashsamtools sort -n -o namesorted.bam sample.bamSorts by read name instead of coordinate. Needed for specific steps like samtools fixmate that require mate pairs adjacent to each other, not for general use, a name-sorted BAM can't be indexed and most tools expect coordinate order. Only produce this if the next tool in your pipeline specifically asks for it.
Coordinates, strand, names, builds
SAM and BAM encode the identical coordinate system: 1-based, closed leftmost mapping position (POS), matching the rest of this pair. Converting one to the other doesn't transform position, CIGAR, or FLAG values, this is a lossless re-encoding, not a coordinate change. The one wrinkle: BAM's binary layout stores POS internally as 0-based for compact packing, but samtools and everything built on htslib convert it back to 1-based the instant you look at it with view or any query. You'd only ever see the raw 0-based value if you wrote your own BAM parser instead of using samtools, htslib, or pysam.
Strand lives in FLAG bit 0x10 in both formats and is untouched by conversion. Chromosome naming (chr1 vs 1) and contig lengths live in the @SQ header lines, which samtools view copies through automatically, but only if they're present in the input. A header-less SAM (for example, one produced upstream by samtools view without -h) converts into a header-less or broken BAM, and that failure often doesn't surface until indexing or a downstream tool chokes on missing contig lengths, not at conversion time.
Genome build isn't tracked anywhere in SAM/BAM natively; the header records contig names and lengths, not which build they came from. Matching contig names and lengths across two BAMs is not proof of matching genome build. The other metadata that matters here is the @HD SO: tag: samtools sort sets SO:coordinate in the header, and downstream tools trust that tag instead of re-scanning the file, so hand-editing or concatenating BAMs without updating it produces files that claim to be sorted but aren't.
Check the output before you trust it
01Read counts match between SAM and BAM
bashsamtools view -c input.sam samtools view -c output.bamExpected Identical non-header record counts before and after conversion; any difference means you converted a truncated or wrong file.
02Sort order tag is set in the header
bashsamtools view -H output.bam | grep ^@HDExpected The @HD line includes SO:coordinate, confirming samtools sort actually ran rather than the file merely appearing sorted.
03Index file exists and region queries work
bashsamtools view output.bam chr1:1-1000 | headExpected A .bam.bai (or .csi) file sits next to the BAM, and a region query returns reads with no idx_find_and_load error.
04flagstat numbers match the aligner's own report
bashsamtools flagstat output.bamExpected Mapped % and total read count are close to what the aligner logged at alignment time.
05Contig count in the BAM header matches the reference FASTA
bashsamtools view -H output.bam | grep -c ^@SQExpected Number of @SQ lines equals the number of sequences in the reference used for alignment.
Errors you will see, and what they mean
- [E::idx_find_and_load] Could not retrieve index file / fail to load the index
- Cause: You ran a region query (samtools view file.bam chr1:1-1000) or handed the BAM to a tool needing random access, but never ran samtools index, or you indexed a name-sorted BAM instead of a coordinate-sorted one. Fix: Coordinate-sort with samtools sort -o sorted.bam file.bam, then samtools index sorted.bam, and point downstream tools at the sorted file.
- samtools index fails or exits non-zero on a freshly converted BAM
- Cause: The BAM went straight from samtools view -b to samtools index with no sort step in between; the spec requires coordinate-sorted input for indexing and samtools index will not sort it for you. Fix: Insert samtools sort between the view step and the index step; never index the raw output of samtools view -b directly.
- [E::bgzf_read] Read block operation failed with error -1 after 0 of 4 bytes, or IGV reports the BAM as corrupted
- Cause: You ran something like samtools sort input.bam -o input.bam, using the same filename for input and output, the write started truncating the file while it was still being read. Fix: Always sort to a new filename, confirm the command finished successfully, then mv the result over the original if you want to reuse the name.
- Downstream tool reports zero or almost zero @SQ lines, or complains the header is missing or invalid
- Cause: The input SAM had no header lines, often because it came from samtools view upstream without -h, so conversion produced a BAM with an incomplete header. Fix: Regenerate the SAM with -h included, or convert from the original aligner output or a BAM that still has its full @HD/@SQ header.
- flagstat shows an implausible mapped percentage right after conversion
- Cause: Either the wrong file got converted, or the original alignment run was interrupted or piped into a truncated SAM before conversion happened. Fix: Check samtools view -c on the source SAM against the aligner's own log before trusting anything computed on the BAM.
Questions people ask
- Do I need the -S flag in samtools view -b?
No. Modern samtools autodetects SAM input from its content, so -S is a no-op kept only for backward compatibility with older scripts and tutorials. It's harmless to include or leave out.
- Can I skip sorting and index the BAM right after converting from SAM?
No. samtools index requires coordinate-sorted input, per the SAM/BAM spec. Convert to BAM, sort by coordinate, then index, in that order; indexing the raw output of samtools view -b will fail.
- Why is my BAM barely smaller than the original SAM?
Check that you actually included the -b flag, forgetting it writes SAM text with a .bam extension, which won't compress or behave like a real BAM. Also expect less shrinkage on files with long CIGAR strings or many custom tags, which compress worse than plain sequence.
- What's the difference between a .bai and a .csi index?
samtools index writes a .bai by default, which is what most workflows expect. Use -c to force a .csi index instead, which supports chromosomes larger than about 512 Mb that overflow the .bai coordinate range.
- Does converting SAM to BAM drop any fields like read groups or quality scores?
No. The conversion is lossless for every field in the record; samtools view -b re-encodes the same data in binary, it doesn't drop tags, quality strings, or header lines that were present in the SAM.
Related pages
Related reading on the blog
Sources
- SAMtools documentation - samtools view and sort commands — Source for the view -b / -S behavior, sort's coordinate-sort requirement for indexing, -@ threading, -m memory cap, and -n name-sort flag.
- SAM/BAM format specification — Authoritative source for coordinate conventions and the requirement that indexed files be coordinate-sorted.
- GATK: BuildBamIndex (Picard) — Alternative to samtools index if your pipeline is already Picard/GATK-based.