Skip to content

KeyError in get_scoring_dict_from_csv when an input antibody has an ultralong CDR3 (TCRMatch returns no output) #1

Description

@dylkot

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:

  1. get_tcr_output_files() runs TCRMatch per CDR region; for CDRH3 the output is empty, so
    the cdrh3 *_tcroutput file is empty.
  2. 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.
  3. 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)

  1. 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.
  2. 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.
  3. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions