# Upstream reports

Most of what this fork fixes is also present in Biopython, because most of it
predates the fork. Where that is true the defect is worth reporting upstream,
so that people who use Biopython benefit whether or not they ever hear of this
project.

This file records what has been reported, what is queued, and what was checked
and deliberately not reported. It is a working record rather than a plan of
campaign: entries leave the queue as often because they turn out to be already
known as because they get filed.

## How reports are made

- **As information, not as pull requests.** A report carries a reproduction, a
  root cause and a suggested fix, and offers a PR rather than opening one.
  Upstream sets its own contribution terms and this fork does not test them.
- **One at a time.** A queue of verified defects is not a reason to file a
  queue of issues. Reports are spaced, and the reception of the last one
  informs whether to send the next.
- **Verified immediately before filing.** Upstream is active. Every report is
  re-checked against current `master` on the day it is sent, because a defect
  found months ago may have been fixed since.
- **Calibrated.** Each report states what was run and what was only read, and
  names what was not checked. Where a fix is a behaviour choice rather than a
  correction, the report says so and leaves the choice upstream.
- **Human-written.** The report is written by a person in their own words, not
  generated text relayed under a name; the findings behind it may come from AI
  work. See [How this fork engages upstream](#how-this-fork-engages-upstream).

## Reported

| upstream | date | subject | fixed here |
|---|---|---|---|
| [#3771](https://github.com/biopython/biopython/issues/3771) | 2026-07-30 | Use-after-free in the `cpairwise2` sequence conversion; root cause for a crash open since 2021 | [#33](https://github.com/dbolser/BioPAIthon/pull/33) |
| [#5269](https://github.com/biopython/biopython/issues/5269) | 2026-07-30 | `run_cealign` segfaults on a zero fragment size, short coordinate entries, or tuples | no |
| [#5272](https://github.com/biopython/biopython/issues/5272) | 2026-08-03 | `PrintedAlignmentParser.feed()` does not bound `offset`, and its length check cannot fire | [#2](https://github.com/dbolser/BioPAIthon/pull/2) |
| email | 2026-08-03 | Out-of-bounds write in `bcifhelpermodule.c` — to the `Bio/PDB` owners under their `SECURITY.md` | [#3](https://github.com/dbolser/BioPAIthon/pull/3) |
| [#5252 review](https://github.com/biopython/biopython/pull/5252#issuecomment-5171465860) | 2026-08-03 | Review of their in-flight endian fix: the output array is still allocated little-endian | [#36](https://github.com/dbolser/BioPAIthon/pull/36) |

## How this fork engages upstream

This fork's own tree welcomes AI-written contributions — that is the reason it
exists, and `ADOPTED.md` is full of them. Reaching *into* Biopython is a
separate act, and the fork chooses to make it on Biopython's terms rather than
its own, because the recipient's comfort is the whole value of the contact.
These are the fork's standing choices for that outbound contact, not rules
imposed on its own work.

- **Outbound writing is a person's own words.** An issue, a comment or an email
  sent to Biopython is written by Dan himself and not signed as AI-authored.
  Biopython has said it prefers discussion text to be human-written (peterjc on
  [#5252](https://github.com/biopython/biopython/pull/5252#issuecomment-5171465860):
  "all written text on the discussions ... should be human"), and meeting that
  preference is a courtesy the contact depends on. The *findings* behind the
  writing may still come from AI work — upstream said in the same breath that it
  expects bug reports found with AI tools — but rewriting rather than
  transcribing is the line: generated prose under a human name would keep the
  letter and lose the point.
- **Code goes to upstream as a description, not a patch.** A report says what a
  fix must guarantee and leaves upstream to write it; where a patch already
  exists here it is offered for a clean-room reimplementation rather than
  submitted. This is the deliberate inverse of how the fork treats the same
  work internally, where the patch is adopted directly with its author's name
  on it (`ADOPTED.md`). Upstream's own policy on AI-written code is still a
  draft at [#5241](https://github.com/biopython/biopython/pull/5241) and not
  agreed, so it is not described here as settled.

## Queued

Verified unreported in the GitHub tracker and still present on upstream
`master`. Ordered by how self-evident the defect is, not by severity.

| # | subject | note |
|---|---|---|
| 1 | `Record.__str__` corrupts a string-valued `wgs` into `A-B-C-D-...` | New in 1.88, and the incomplete half of [#5274](https://github.com/biopython/biopython/issues/5274). [#5275](https://github.com/biopython/biopython/pull/5275) fixed the parsed-list path but left `__init__` at `self.wgs = ""`, so a string assignment now serialises one character at a time. `wgs_scafld` was corrected to `[]` in the same commit. Comment on #5275. **Fixed here in [#62](https://github.com/dbolser/BioPAIthon/pull/62)**, so the report describes a defect this tree no longer has. |
| 2 | `RestrictionBatch.format` docstring still promises expression evaluation | Documentation only. The 1.88 `eval` removal changed the behaviour and left the docstring saying "If y can be evaluated to a RestrictionType". **Fixed here in [#62](https://github.com/dbolser/BioPAIthon/pull/62).** |
| 3 | `Bio.File.as_handle` re-catches a `TypeError` from the caller's own block | Fixed upstream in 2018 for [#1544](https://github.com/biopython/biopython/issues/1544), reverted by `f09b169a4` in 2020, no regression test. Comment on #1544 rather than a new issue. |
| 4 | `_get_pi` builds `forward_table.keys() + stop_codons` | `TypeError`; the `F1x4` and `F61` codon frequency models are unusable. |
| 5 | `Alignment.format()` reports any `AttributeError` as "not yet implemented" | Upstream's own `Bio.Align.write()` already does this correctly. |
| 6 | `CodonAdaptationIndex` rejects `MutableSeq` | The docstring promises it is accepted. |
| 7 | GAF by-protein iterators never yield the last protein | Silent data loss. Fixing it changes output for every caller. |
| 8 | `Seq.search()` never matches at the final position | Off by one since the method shipped. |
| 9 | `six_frame_translations` misaligns the reverse frames | Correct only when `len(seq) % 3 == 1`. Display only. |
| 10 | `_G_test` divides by zero on any empty cell | `mktest` crashes on identical sequences. |
| 11 | `nt_search(seq, "")` never terminates | The fix is an API choice; report, do not prescribe. |
| 12 | `Tm_GC(valueset=0)` raises `UnboundLocalError` | The guard admits a value no branch handles. |
| 13 | `str()` on an `index_db` dictionary raises `AttributeError` | Upstream carries a `TODO` about this exact question; lead with it. |
| 14 | `SeqIO.index()` raises `IndexError` on a bare `>` FASTA header | `parse()` reads the same record as `id=""`. |
| 15 | Two `except ValueError` clauses in `SeqIO/_index.py` cannot fire | File the `tab` and `genbank` halves separately; cite [#1344](https://github.com/biopython/biopython/issues/1344). |
| 16 | `reverse_complement()` cannot sort features with an `UnknownPosition` | Cite [#1772](https://github.com/biopython/biopython/issues/1772), same cause, different path. |
| 17 | `ProteinAnalysis.flexibility()` reads the wrong window centre | Comment on [#4170](https://github.com/biopython/biopython/pull/4170), which fixes the adjacent window-count bug only. |
| 18 | `neighbor_simple_search` re-sorts the KD-tree's shared point list | `DataPoint_sort` at `kdtrees.c:1226` reorders the list the built tree's node ranges index into, so a later `search()` on the same `KDTree` silently misses points — measured 623 → 397 hits on 2,000 points. Silent wrong answer. **Fixed here in [#74](https://github.com/dbolser/BioPAIthon/pull/74)**, which sorts a private copy so the tree is immutable after construction; the fork's fix is bundled with a larger rewrite, so describe the defect, not the patch. |
| 19 | `SeqIO.index()` on pretty-printed UniProt XML is silently empty | The `b"<entry "` marker at `_index.py:198` is matched at line start only; an indented `<entry>` never matches, `parse()` reads the same file fine. `Tests/SwissProt/multiligand.xml` in both trees demonstrates. **Fixed here in [#88](https://github.com/dbolser/BioPAIthon/pull/88)** as a side effect of moving id rules onto the parsers — the narrow fix upstream is a `lstrip` or regex allowance. |
| 20 | FOGSAA sizes its matrix in `int` arithmetic | `PyMem_Calloc((nA+1) * (nB+1), ...)` at `_pairwisealigner.c:6075` and the matching `MATRIX()` index macro wrap for two sequences ≳46,341 nt, under-allocate, and corrupt the heap — segfault from pure Python, reproduced in this tree pre-fix. **Fixed here in [#67](https://github.com/dbolser/BioPAIthon/pull/67)** with checked `size_t` sizing. Caller-driven, not file-driven, so the public tracker is the right venue (same reasoning as #5272). |
| 21 | [#3911](https://github.com/biopython/biopython/pull/3911)'s newline check never matches list-valued qualifiers | `"\n" in feature.qualifiers[k]` is element membership on a list, so the list-of-strings shape the motivating SnapGene case produces is untouched by the fix. Comment on #3911 rather than a new issue. Fork adopted the PR and corrected this in [#80](https://github.com/dbolser/BioPAIthon/pull/80). |
| 22 | `System.stochiometry()` has never worked | Three defects in one method, all verbatim from the 2001 import: `[] * n` is `[]`, rows created as the integer `0`, and coefficients written with no row index. Any non-empty system raises `IndexError`; nothing in either tree calls it, which is how it survived 24 years. **Fixed here in [#97](https://github.com/dbolser/BioPAIthon/pull/97)**; found by mypy's `check_untyped_defs` during [#96](https://github.com/dbolser/BioPAIthon/pull/96). |

## Needs work before it can be filed

- **`translate()` drops an explicit `gap=` for `Seq` input.** Open PR
  [#4994](https://github.com/biopython/biopython/pull/4994) changes the
  module-level default and does not forward the argument, so this survives it.
  Any report must say so or it will be closed as a duplicate.
- **`SeqIO.index()` masks a proxy's `TypeError`.** The only reproduction is a
  patched SFF header. Needs a natural malformed input first.
- **Location truthiness.** The dead Python 2 `__nonzero__` methods and the
  stale docstring are real, but the resolution is a behaviour choice, and
  either direction touches `Bio/SeqIO/SnapGeneIO.py:180`.
- **`cpairwise2.rint` parses `int` with `"l"`.** A genuine LP64 stack
  overwrite, but reachable only from the deprecated `Bio.pairwise2`.

- **TwoBit `__getitem__` mis-slices three ways.** Reproduced here and on
  stock Biopython 1.85: `_twoBitIO.c` truncates `(end - start) / step`
  instead of ceiling-dividing, so extended slices silently drop the final
  base (`seq[0::3]` loses the last codon position unless `len % 3 == 0`);
  negative-step slices compute a negative byte count and raise
  `RuntimeError` (`seq[::-1]`); and an integer index equal to the record
  length reads a base from the *next* record's packed data instead of
  raising `IndexError`. Needs a duplicate-check against the tracker and
  re-verification on `master` before filing.
- **`BgzfWriter` crashes on poorly-compressible input.** ~64 KB of
  high-entropy data deflates past the 65,536-byte block cap and raises
  `RuntimeError: TODO - Didn't compress enough`; compressed sizes
  65,511–65,536 slip the guard and fail later in `struct.pack("<H", ...)`.
  htslib caps per-block input instead. Reproduced on 1.85; needs the same
  duplicate-check before filing.

## Disclosed, in progress

Both of the memory-safety findings have now been passed on. Neither is closed.

- **`Bio/PDB/bcifhelpermodule.c` writes past the output buffer.** Sent by email
  to the `Bio/PDB` owners on 2026-08-03 under their `.github/SECURITY.md`,
  rather than to the public tracker, because it is driven by file content. The
  patch is AI-written, so upstream is reimplementing it clean-room from the
  report; Will Tyler has been asked to take it. A MITRE CVE request is agreed
  in principle and not yet submitted — it should wait until the fix has a
  public reference. Peter Cock confirmed their only precedent is
  CVE-2025-68463, assigned by MITRE at the reporter's request.
- **`Bio/Align/_aligncore.c` returns heap contents.** Filed publicly as #5272,
  since it needs a caller to pass malformed input rather than arriving in a
  downloaded file. Different module and different owner (@mdehoon) from the
  BinaryCIF one.

## Checked and not reported

- **`ProteinAnalysis.flexibility()` window count.** Already open as
  [#4170](https://github.com/biopython/biopython/pull/4170) since 2022.
- **The `_prim` spanning-tree seed.** Already open as
  [#2301](https://github.com/biopython/biopython/pull/2301) since 2019, with
  the same one-line fix and maintainer confirmation.
- **`Bio.PDB.ccealign` hanging on specific structures**
  ([#4888](https://github.com/biopython/biopython/issues/4888)). Reproduced on
  1.83 and confirmed fixed from 1.84, but the change was a wholesale rewrite of
  the module rather than a single commit, so there is nothing useful to add.
- **The zero-width-space crash** ([#3387](https://github.com/biopython/biopython/issues/3387),
  closed). Plausibly the same defect as #3771 but not reproducible on any
  version tried, so no claim was made.
- **The second commit of [#5127](https://github.com/biopython/biopython/pull/5127)
  breaks `coord_space`.** Already raised. peterjc posted mypy's output on
  2025-12-27, the day after the pull request opened, naming
  `sc[2][0]` at lines 572 and 619 — "Value of type `float` is not indexable" —
  and asking the author to avoid the double indexing. The thread has not moved
  since; the pull request was approved by JoaoRodrigues and last saw activity in
  February 2026.

  This fork adopted the first commit only, and confirmed the second fails at
  runtime with `IndexError: invalid index to scalar variable` against upstream
  `master` `e136be720`, whose `test_PDB_vectors` still passes flat arrays.

  Two things neither the linter nor the thread has, kept here in case it
  revives. First, the root cause: each changed line sits under a commented-out
  reference line using `[0][0]`, written when the callers passed column arrays;
  the callers switched to flat ones and the comments were never updated, so the
  commit is faithfully following stale documentation. Second, **the suggested
  fix does not work either** — `get_spherical_coordinates` returns a *tuple*,
  so `sc[2, 0]` raises `TypeError: tuple indices must be integers or slices,
  not tuple`. The correct expression is the original `sc[2]`.

## Limits of the survey

The duplicate check covered the GitHub issue and pull request tracker only. It
did not cover the `biopython-dev` mailing list or the pre-GitHub Redmine and
Bugzilla archives, so "unreported" here means "not in the GitHub tracker".
