@@ -230,11 +230,13 @@ std::string VCFEntry::getAltString(const SeqLib::BamHeader& header) const {
230230 // SV breakends get the BND-style `N]chr:pos]` mate notation.
231231 const std::string ref = getRefString ();
232232
233+ // +1: positions are 0-based internally (htslib); the mate locus in BND
234+ // notation is 1-based per the VCF spec, matching POS in toFileString.
233235 std::stringstream ptag;
234236 if (id_num == 1 ) {
235- ptag << bp->b2 .gr .ChrName (header) << ' :' << bp->b2 .gr .pos1 ;
237+ ptag << bp->b2 .gr .ChrName (header) << ' :' << ( bp->b2 .gr .pos1 + 1 ) ;
236238 } else {
237- ptag << bp->b1 .gr .ChrName (header) << ' :' << bp->b1 .gr .pos1 ;
239+ ptag << bp->b1 .gr .ChrName (header) << ' :' << ( bp->b1 .gr .pos1 + 1 ) ;
238240 }
239241
240242 std::stringstream alt;
@@ -435,17 +437,20 @@ std::string VCFEntry::toFileString(const SeqLib::BamHeader& header,
435437 // positions (the event's 5' anchor); the INFO/END field carries the
436438 // other boundary. For BND records, POS is just the breakend this
437439 // entry represents (legacy behavior).
440+ // +1: breakend positions are stored 0-based internally (htslib
441+ // convention); VCF POS is 1-based. This mirrors BreakPoint::toFileString
442+ // (bps.txt.gz) and the END INFO field below, both of which add +1.
438443 int pos;
439444 std::string chr_name;
440445 if (symbolic_rep) {
441- const int p1 = bp->b1 .gr .pos1 ;
442- const int p2 = bp->b2 .gr .pos1 ;
446+ const int p1 = bp->b1 .gr .pos1 + 1 ;
447+ const int p2 = bp->b2 .gr .pos1 + 1 ;
443448 pos = std::min (p1, p2);
444449 // Both breakends live on the same chrom when symbolic; use b1's.
445450 chr_name = bp->b1 .gr .ChrName (header);
446451 } else {
447452 const BreakEnd* be = (id_num == 1 ) ? &bp->b1 : &bp->b2 ;
448- pos = be->gr .pos1 ;
453+ pos = be->gr .pos1 + 1 ;
449454 chr_name = be->gr .ChrName (header);
450455 }
451456
0 commit comments