Summary
run_bcrmatch.py crashes with a KeyError whenever any input antibody has a CDR that
TCRMatch silently fails to score — in practice, an ultralong CDR3 (e.g. the
disulfide-bonded "stalk-and-knob" CDR3s found in some real antibodies). TCRMatch emits an
empty result for that whole CDR region's file, so the region's score dict is empty, and
get_scoring_dict_from_csv() then indexes it with a pair key taken from a different
region → KeyError. One such antibody anywhere in the input takes down the entire run.
Environment
- BCRMatch commit
7962c96 (2025-06-04)
- TCRMatch commit
e2c1bf8
- Reproduced both with the prebuilt Docker image and a local install (Python 3.9,
tensorflow 2.8.0).
Steps to reproduce
Take BCRMatch's own bundled example (examples/set-a/example.tsv, which runs fine) and add
one antibody with an ultralong CDR3. The extra antibody below is a real SARS-CoV-2
antibody from CoV-AbDab (Huang_SKD); its CDRH3 is 63 aa.
example_with_ultralong_cdr3.tsv (attached):
Seq_Name CDRL1 CDRL2 CDRL3 CDRH1 CDRH2 CDRH3
1 NNIGSKS DDS WDSSSDHA GFTFDDY SWNTGT RSYVVAAEYYFH
2 SQDISNY YTS DFTLPF GYTFTNY YPGNGD GGSYRYDGGFD
3 ASGNIHN YYT HFWSTPR GFSLTGY WGDGN RDYRLD
4 SESVDNYGISF AAS SKEVPL GYTFTSS HPNSGN RYGSPYYFD
5 ASQDISN YFT QYSTVPW GYDFTHY NTYTGE PYYYGTSHWYFD
ultralong SSNVGNGY GDT ASAEDSSSNAV GFSLSDKA IDTGGNT TTVHQRTSEKRSCPGGSSRRYPSGASCDVSGGACACYVSNCRGVLCPTLNEIVAYTYEWHVDA
Run (Docker):
docker run --rm -v $(pwd):/data bcrmatch bash -c \
"python3 run_bcrmatch.py -i /data/example_with_ultralong_cdr3.tsv -tn abpairs_abligity -o /data/out.csv"
Actual result
Retrieving all files containing the TCRMatch result...
Retrieve scores...
Traceback (most recent call last):
File ".../run_bcrmatch.py", line 564, in <module>
main()
File ".../run_bcrmatch.py", line 547, in main
score_dict = get_scoring_dict_from_csv(tcrout_files)
File ".../run_bcrmatch.py", line 320, in get_scoring_dict_from_csv
all_score_dict[pair].append(dict_3[pair])
KeyError: '1_1'
The identical input without the last row (i.e. the stock 5-antibody example) completes
normally and produces 25 pairs.
Root cause
TCRMatch returns no output at all for a sequence file that contains an ultralong CDR3 —
not even a header. Minimal demonstration (cdrh3_ultralong_plus_normal.txt contains the
63-aa CDR3 plus one normal CDR3):
$ tcrmatch -i cdrh3_ultralong_plus_normal.txt -t 1 -s 0 -d cdrh3_ultralong_plus_normal.txt
# (prints nothing; 0 rows)
# the same file with only the normal CDR3 prints a header + scored rows as expected
Consequently, in run_bcrmatch.py:
get_tcr_output_files() runs TCRMatch per CDR region; for CDRH3 the output is empty, so
the cdrh3 *_tcroutput file is empty.
compile_scores() on that empty file returns an empty dict, so dict_3 (cdrh3) has no
keys, while dict_1/dict_2/dict_4/dict_5/dict_6 are fully populated.
- The loop at line ~315 iterates
for pair in dict_1.keys(): ... dict_3[pair], which
assumes every region shares the same pair keys → KeyError on the first pair ('1_1').
So a single TCRMatch-unsupported CDR3 makes the entire run fail, and the error message
gives no hint as to which input (or region) caused it.
Suggested fixes (any one helps)
- Don't assume all six region dicts share keys. Iterate over the intersection of keys
(a pair needs all six CDR scores to be scoreable anyway), or use .get() and skip pairs
missing from any region. This alone prevents the crash.
- Detect empty TCRMatch output for a region and raise a clear, actionable error (or
warning) that names the offending region/sequence, instead of a downstream KeyError.
- Validate CDR3 length on input and warn/skip antibodies whose CDR3 exceeds what
TCRMatch can process, so users know those antibodies were dropped.
Happy to send a PR for option 1 if useful.
example_with_ultralong_cdr3.tsv
cdrh3_ultralong_plus_normal.txt
Summary
run_bcrmatch.pycrashes with aKeyErrorwhenever any input antibody has a CDR thatTCRMatch silently fails to score — in practice, an ultralong CDR3 (e.g. the
disulfide-bonded "stalk-and-knob" CDR3s found in some real antibodies). TCRMatch emits an
empty result for that whole CDR region's file, so the region's score dict is empty, and
get_scoring_dict_from_csv()then indexes it with a pair key taken from a differentregion →
KeyError. One such antibody anywhere in the input takes down the entire run.Environment
7962c96(2025-06-04)e2c1bf8tensorflow 2.8.0).
Steps to reproduce
Take BCRMatch's own bundled example (
examples/set-a/example.tsv, which runs fine) and addone antibody with an ultralong CDR3. The extra antibody below is a real SARS-CoV-2
antibody from CoV-AbDab (
Huang_SKD); its CDRH3 is 63 aa.example_with_ultralong_cdr3.tsv(attached):Run (Docker):
Actual result
The identical input without the last row (i.e. the stock 5-antibody example) completes
normally and produces 25 pairs.
Root cause
TCRMatch returns no output at all for a sequence file that contains an ultralong CDR3 —
not even a header. Minimal demonstration (
cdrh3_ultralong_plus_normal.txtcontains the63-aa CDR3 plus one normal CDR3):
Consequently, in
run_bcrmatch.py:get_tcr_output_files()runs TCRMatch per CDR region; for CDRH3 the output is empty, sothe cdrh3
*_tcroutputfile is empty.compile_scores()on that empty file returns an empty dict, sodict_3(cdrh3) has nokeys, while
dict_1/dict_2/dict_4/dict_5/dict_6are fully populated.for pair in dict_1.keys(): ... dict_3[pair], whichassumes every region shares the same pair keys →
KeyErroron the first pair ('1_1').So a single TCRMatch-unsupported CDR3 makes the entire run fail, and the error message
gives no hint as to which input (or region) caused it.
Suggested fixes (any one helps)
(a pair needs all six CDR scores to be scoreable anyway), or use
.get()and skip pairsmissing from any region. This alone prevents the crash.
warning) that names the offending region/sequence, instead of a downstream
KeyError.TCRMatch can process, so users know those antibodies were dropped.
Happy to send a PR for option 1 if useful.
example_with_ultralong_cdr3.tsv
cdrh3_ultralong_plus_normal.txt