Fix dash/empty ALT alleles for deletions in VCF I have a VCF file that has deletions specified like this, with the REF allele containing only the deleted portion and the ALT allele just saying -:

chr1 101 . AAA -


I would like to combine this with another VCF in which deletions are specified like this, with REF and ALT both extended by 1bp to the left to ensure neither is empty:

chr1 100 . GAAA G


Is there a tool to convert from one of these representations to the other? I have already tried vt normalize and bcftools norm but neither of them seem to do this.

I don't really mind which representation I end up with although I think the second is the one more consistent with the spec.
According to the specs the ALT is not allowed to contain -.
Here is a little python (&gt;=3.6) script that is able to fix it. It makes use of the pysam module. You need a reference file and this fasta file must be indexed by samtools faidx reference.fa. Please note that it doesn't support multiple values in the ALT column for records that contain a -.
Usage:
python fixvcf.py -f reference.fasta -o out.vcf in.vcf

The script:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import argparse
import sys

import pysam


class Application:
    def __init__(self):
        self.args = Application.get_args()
        self.vcf = pysam.VariantFile(sys.stdin)
        self.fasta = pysam.FastaFile(self.args.fasta)

    @staticmethod
    def get_args() -&gt; argparse.Namespace:
        &quot;&quot;&quot;
        Read command line arguments and set input and output sources.

        :return: parsed command line arguments
        &quot;&quot;&quot;
        parser = argparse.ArgumentParser(prog=&quot;fixvcf.py&quot;, description=&quot;Fixes deletion represented by -&quot;)
        parser.add_argument('--version', action='version', version='%(prog)s 0.1')

        parser.add_argument(&quot;input&quot;, help=&quot;input file, use - to read from stdin&quot;)
        parser.add_argument(&quot;-f&quot;, &quot;--fasta&quot;, help=&quot;fasta reference file&quot;, required=True)
        parser.add_argument(&quot;-o&quot;, &quot;--output&quot;, help=&quot;output file&quot;)

        if len(sys.argv) == 1:
            parser.print_help(sys.stderr)
            sys.exit(1)

        args = parser.parse_args()

        if args.output:
            sys.stdout = open(args.output, &quot;w&quot;)

        if args.input != &quot;-&quot;:
            sys.stdin = open(args.input, &quot;r&quot;)

        return args

    def fix_record(self, record: pysam.libcbcf.VariantRecord) -&gt; pysam.libcbcf.VariantRecord:
        &quot;&quot;&quot;
        If the ALT column of the records contains a `-`, the variant position is shift one position to the left.
        For reaching this the reference base for the position is determined and prepended to the current REF bases.
        The ALT value `-` is replaced with the determined ref base on position before. In case there are multiple ALT
        values the determined ref base is prepended to them.

        :param record: vcf file record
        :return: fixed vcf record
        &quot;&quot;&quot;
        if &quot;-&quot; in record.alts:
            ref = self.fasta.fetch(region=f&quot;{record.chrom}:{record.pos - 1}-{record.pos - 1})&quot;)    
            record.pos = record.pos - 1
            record.ref = f&quot;{ref}{record.ref}&quot;
            record.alts = tuple(map(lambda alt: ref if alt == &quot;-&quot; else f&quot;{ref}{alt}&quot;, record.alts))

        return record

    def start(self):
        &quot;&quot;&quot;
        Print the header of the vcf file. Afterwards the fix_record method is applied to each record in the vcf file.

        For more information about python's map() see: https://docs.python.org/3.6/library/functions.html#map

        For more information about the unpacking mechanism using `*`see:
        https://docs.python.org/3/tutorial/controlflow.html#tut-unpacking-arguments
        &quot;&quot;&quot;
        print(self.vcf.header, end=&quot;&quot;)
        print(*map(self.fix_record, self.vcf), sep=&quot;&quot;, end=&quot;&quot;)


if __name__ == &quot;__main__&quot;:
    app = Application()
    app.start()

A lot of the code above is boilerplate for running a script on the command line. The critical code for the transformation is in the fix_record function. The relevant code is below, expanded, with line numbers and a description of each command.
[1]   ref = self.fasta.fetch(
          region=f&quot;{record.chrom}:{record.pos - 1}-{record.pos - 1})&quot;
      )
[2]   record.pos = record.pos - 1
[3]   record.ref = f&quot;{ref}{record.ref}&quot;
[4]   record.alts = tuple(
          map(
              lambda alt: ref if alt == &quot;-&quot; else f&quot;{ref}{alt}&quot;,
              record.alts
          )
      )


This grabs the base preceding the inserted/deleted sequence.
This corrects the position of the variant to account for prepending another nucleotide.
This defines the new corrected REF allele: concatenate the base grabbed in [1] with the original REF allele
Apply an anonymous (lambda) function to each ALT allele; the function will replace - with the base grabbed in [1], or preprend the base to the allele if it is not -