# Handling Errors in Bioinformatics Python Scripts

Most failures in python for bioinformatics work come from two mundane sources: the script cannot find its input file, or the file is not shaped the way the code assumed. The first thing to check is also the cheapest one. Read the last line of the traceback, the report Python prints when a script crashes. It names the exception type and the value that triggered it, and that single line resolves a large share of everyday failures before you touch any code.

The harder failures arrive once a script becomes a pipeline. Modern analyses chain many programs, sometimes a dozen or more. PePApipe, an automated pipeline for African Swine Fever Virus genomes, integrates thirteen software tools spanning raw read quality control, genome assembly, and variant calling [1]. The PyMACS framework for [molecular dynamics simulations](/knowledge/bioinformatics/molecular-dynamics-simulations-of-proteins-and-force-fields) coordinates steps from structure cleanup through trajectory analysis and states plainly that errors at any stage can compromise results [2]. In a chain like that, one unhandled exception wastes queued compute hours and leaves misleading partial outputs behind.

This article covers the causes of script failures from most common to rarest, explains the mechanism behind each one, gives a diagnostic sequence you can follow at the terminal, and closes with the habits that prevent the next failure. The solutions apply whether you write ten-line conversion utilities or hundred-step analysis pipelines.

## Read the Traceback First: The Five-Minute Diagnostic

![Five ordered steps for handling a Python crash, from reading the final exception line to checking the raw file from the shell.](/article-images/illustrations/python-bioinformatics-error-handling-traceback-diagnostic.webp)

When Python crashes it prints a traceback, the ordered record of function calls that led to the failure. Python lists the oldest call first and the crash site last, so the final line carries the exception type and message. In a notebook the same report appears under the failing cell, and long traces from deep library calls resolve the same way.

A typical crash looks like this:

```python
Traceback (most recent call last):
  File "gc_profile.py", line 22, in <module>
    gc = gc_fraction(record.seq)
  File "gc_profile.py", line 9, in gc_fraction
    return 100.0 * (seq.count("G") + seq.count("C")) / len(seq)
ZeroDivisionError: division by zero
```

Read it bottom up. The last line says the division failed because `len(seq)` returned zero, so an empty sequence reached the function. The frames above show where that value came from, and the filename and line number point at the exact spot.

A short diagnostic sequence covers most situations:

1. Read the final line. The exception name narrows the fault class immediately. `FileNotFoundError` points at paths, `KeyError` at a missing dictionary key or table column, `MemoryError` at dataset size.
2. Move upward to the first frame that names your own script rather than a library. Bad data usually enters your code there.
3. Rerun with the smallest input that still fails. One FASTA record, one sample, one chromosome.
4. At the failing line, print the type and exact content of the suspect value with `print(type(value), repr(value))`.
5. Inspect the raw file from the shell. `head` shows the first lines, `wc -l` counts lines, `file` reports compression and encoding, and `md5sum` confirms you have the file copy you think you have.

Resist changing code before steps 1 through 4 are done. Edits made before the fault is understood usually treat symptoms and hide the real cause one layer deeper.

## What Causes Python Bioinformatics Errors

Script failures follow a frequency gradient. Path and file problems interrupt runs most often. Malformed data comes next, then type and missing-value surprises, then resource limits, then failures in external tools, and finally silent logic errors, which surface last and do the most damage precisely because nothing crashes.

### Wrong Paths and Missing Files

`FileNotFoundError` is the most common crash in practice, and the missing file usually exists. The mechanism is the working directory, the folder a script treats as its starting point when resolving relative paths. That directory differs between your interactive shell, a notebook server, and a scheduled job. PePApipe runs either locally through bash scripts or under Slurm for batch processing of multiple samples [1], and a batch job can start somewhere other than the directory where you tested. A relative path that worked interactively then points at nothing.

Confirm the fault by printing the working directory and the resolved path:

```python
from pathlib import Path
import os

print(os.getcwd())
print(Path("data/sample.fasta").resolve())
```

Fix it by passing paths as explicit arguments and checking them before use, so the failure message names the absolute path that was actually tried:

```python
fasta = Path(raw_input_path)
if not fasta.is_file():
    raise FileNotFoundError(f"No FASTA at {fasta.resolve()}")
```

The `pathlib` module also absorbs the Windows versus Linux path separator problem, which removes an entire class of cross-machine breakage.

### Malformed and Unexpected Input Data

The text formats used in bioinformatics tolerate mess, and mess is the norm in collected data. FASTA, the format that stores nucleotide or amino acid sequences under header lines, accumulates blank lines and carriage returns from Windows editors. FASTQ, which adds one line of per-base quality scores to each sequence record, breaks when an interrupted download truncates a record so line counts stop being divisible by four. A file ending in `.gz` holds binary compressed data, and opening it with a plain text reader raises `UnicodeDecodeError` or yields garbage. Public database downloads add their own surprises. One automated viral genomics framework downloaded and curated roughly 410 complete ChiLCV genomes from public databases [3], and downloads at that scale inevitably include truncated files and HTML error pages saved with sequence file extensions.

Confirm data problems from the shell before touching Python. `head -4` shows the first FASTQ record, `wc -l` exposes line counts that break the four-line pattern, and `file` distinguishes gzip data from text. Then fix the script by validating structure before analysis and reporting the position of the first bad record rather than a bare exception.

### Missing Values and Wrong Types in Tables

[Tabular data](/blog/research-skills/tabular-data-what-it-is-and-how-to-analyze-it) brings its own failure class. pandas, the standard table library, infers a column's data type from its contents. A read-count column containing a few unrecognized placeholders such as a dash or `ND` becomes object dtype, the generic text type, and arithmetic on it raises `TypeError`. An integer count column with any missing value becomes floating point, so counts display as `5.0` and logic that expects whole numbers breaks quietly. Identifier columns are not safe either, because `read_csv` treats strings like `NA` and `null` as missing markers by default, so an identifier that collides with one of those strings silently becomes NaN, the floating point marker for missing data. NaN then spreads through means, correlations, and table joins without raising anything.

Confirm by checking `df.dtypes` and missing-value counts immediately after loading, before any computation. Fix by deciding per column whether missing values mean drop, fill, or fail, enforcing that decision in code, and asserting required columns and types at load time so a renamed column fails in seconds rather than after an hour of compute.

### Memory Exhaustion on Large Datasets

Loading an entire dataset into RAM works during testing and fails in production. A dense genotype matrix over a million variable sites and a thousand samples is a billion cells. Symptoms include `MemoryError`, a notebook kernel that dies with no traceback at all, or a process the operating system killed.

There is a genuine tradeoff here rather than a simple mistake. SNPio keeps genotype arrays, metadata, and provenance attributes in memory, which made it faster than the R-based tools it was benchmarked against but heavier on memory [4]. Holding data in RAM speeds up repeated operations until the data no longer fits, at which point the whole approach fails at once.

Fix by streaming records with generators, objects that yield one item at a time instead of building a full list, slicing work by chromosome or window, and processing independent chunks in parallel, the approach GlucoStats takes to handle large continuous glucose monitoring datasets efficiently [5]. Streaming has a side effect worth knowing: because nothing is read until you iterate, malformed records surface mid-loop rather than at the load line, which pairs with the Biopython gotcha described later.

### External Tools and Configuration Failing

Scripts that wrap command-line tools inherit another failure class. `subprocess`, the standard module for running external programs, reports failures as nonzero exit codes with diagnostics on stderr, the output stream reserved for error messages. Typical triggers are a tool missing from the active environment, a missing index or database, and configuration files pointing at stale paths. `which samtools` in the same shell that runs the script tells you quickly whether the tool is reachable.

Configuration deserves respect as an input. PyGOLD was built to parse, edit, and write GOLD docking configuration files programmatically because stepping through a graphical interface between the stages of an automated virtual screening workflow is not feasible [6]. The same lesson applies to any script that reads a config file: validate it, log it, and fail early when a required field is missing or a path inside it no longer exists.

Fix by running wrapped commands with `check=True` so a nonzero exit raises immediately, capturing stderr, logging the exact command, and recording tool versions in the run log:

```python
import subprocess

cmd = ["samtools", "flagstat", bam_path]
try:
    result = subprocess.run(cmd, capture_output=True, text=True, check=True)
except subprocess.CalledProcessError as err:
    log.error("Failed (%d): %s", err.returncode, " ".join(cmd))
    log.error("stderr: %s", err.stderr)
    raise
```

### Silent Logic Errors

The rarest and worst class produces no crash at all. The script finishes, the output looks plausible, and the result is wrong. Bioinformatics has a set of recurring traps. Coordinate conventions differ across formats: BED intervals are zero-based and half-open, while VCF and GFF positions are one-based and fully closed, so a variant at the first base of a chromosome is position 1 in a VCF and offset 0 in a BED file. Copying intervals between formats without conversion shifts every coordinate by one. Strand handling, float equality comparisons, and in-place edits of shared data frames round out the list.

Detection beats prevention for this class, because these bugs pass every syntax check. Assert invariants inside the code, and verify a handful of known results by hand after each change. Experience from automated analyses supports the approach. The PubChem workflow-inference project found that most of its errors traced back to specific, identifiable decision points in a project rather than diffuse noise [7], and structured outputs make such points inspectable. pyKVFinder stores its cavity measurements in NumPy arrays that feed directly into plotting and statistics libraries [8], and array or table outputs of that kind make anomalies far easier to spot than loose printed text.

## Troubleshooting Table for Common Failures

Keep this table beside the terminal during a run. The confirmation column matters most, because each check distinguishes the named cause from its neighbors.

| Symptom | Likely cause | How to confirm | Fix |
|--|--|--|--|
| `FileNotFoundError` for a file you can see in the folder | Relative path resolved against a different working directory | Print `os.getcwd()` and `Path.resolve()` | Pass absolute paths as arguments, or anchor paths to the script location |
| `UnicodeDecodeError` when opening a file | The file is gzipped or is not UTF-8 text | Run `file` on the path to see its true type | Open `.gz` files with `gzip.open`, pass `encoding` explicitly for text |
| `IndexError` while reading FASTQ | A truncated record broke the four-line structure | Count lines with `wc -l`, expect a multiple of 4 | Validate each record and report the line position of the failure |
| `KeyError` on a column name or header field | Wrong delimiter, renamed header, or stray whitespace | Print `repr()` of one raw line from the file | Strip whitespace, split on the documented delimiter, assert required keys |
| `TypeError: could not convert string to float` | Header rows or placeholder strings inside a numeric column | Check `df.dtypes` and the first rows after loading | Clean or skip headers, declare `na_values`, then assert types |
| Every downstream statistic comes out NaN | Missing values propagated through joins or aggregations | Count missing values per column right after loading | Decide drop, fill, or fail per column, then enforce the choice |
| `MemoryError`, or a notebook kernel dies silently | The whole dataset was loaded into RAM | Load a small slice first while watching memory | Stream with generators, chunk by chromosome or window, parallelize chunks |
| `subprocess.CalledProcessError` with returncode 1 | The wrapped tool failed, often a missing index or database | Run the exact command by hand and read its stderr | Use `check=True`, capture stderr, log the command and tool versions |
| `ModuleNotFoundError: No module named 'Bio'` | Biopython is absent from the environment that ran the script | Run `python -c "import Bio"` in that environment | Install into the active environment and pin the version |
| Output looks plausible but is subtly wrong | Coordinate convention or strand handling mix-up | Verify one known interval or variant by hand | Convert coordinates explicitly and assert interval boundaries |

## How to Fix Python Bioinformatics Error Handling

Lasting solutions are habits rather than patches. Six of them, applied consistently, turn crashes from mysteries into signposts.

### Validate Input Before You Process It

Add cheap checks at every boundary between your script and the outside world: existence and nonzero size for files, record counts within an expected range, required columns, sequence alphabet, and value ranges such as alignment lengths above zero. A validation failure should name the file, the record, the offending value, and the expectation. Compare the message `bad value` with `Record KX123456 in input.fasta has sequence length 0, expected at least 1`. The second lets a collaborator fix the problem without reading your code, which is the real test of an error message.

### Catch Exceptions Narrowly and Meaningfully

An exception is Python's signal that an operation failed. Wrap the smallest operation that can fail, catch only the exceptions that operation can actually raise, and let everything else crash loudly. A bare `except` clause catches everything, including the interrupt you press to stop a runaway loop, and it converts bugs into silence. When you convert an error into your own message, preserve the original cause so the chain stays visible in the traceback:

```python
import pandas as pd

try:
    table = pd.read_csv(counts_path, sep="\t")
except FileNotFoundError:
    raise RuntimeError(f"Count table missing: {counts_path}") from None
```

Handlers belong at boundaries, around file reads, tool calls, and network requests. Code in the middle of an analysis should assume its inputs are already valid, which keeps the code readable and the tracebacks honest.

### Decide Per Step Whether to Stop or Continue

Batch scripts that loop over samples need an explicit failure policy. Stop the run for failures that invalidate everything downstream, such as a missing reference genome or a corrupt manifest. For many independent items, isolate the failure instead: log it, write the item to a failed-items file, continue with the rest, and exit with a nonzero code at the end if anything failed.

```python
failed = []
for sample in samples:
    try:
        run_qc(sample)
    except (OSError, ValueError) as err:
        log.error("Sample %s failed: %s", sample.id, err)
        failed.append(sample.id)

Path("failed_samples.txt").write_text("\n".join(failed))
if failed:
    raise SystemExit(f"{len(failed)} samples failed, see failed_samples.txt")
```

Structured output layouts make this policy visible. PePApipe classifies generated files into structured folders and keeps intermediates usable as inputs for further or parallel analyses [1], and a fixed layout lets you see at a glance which samples completed and which need a rerun.

### Log With Context Instead of Print

The `logging` module replaces scattered print statements with timestamped, leveled messages that can be redirected to a file without code changes. Log the command line, package versions, and input checksums at startup, then log per-item progress with identifiers rather than bare counters. Provenance, the recorded trail of what was done to which data, is the same idea applied across a whole project. SNPio attaches metadata and provenance-tracking attributes alongside the genotype data it retains [4], and the same instinct in a small script keeps results traceable months later.

```python
import logging

logging.basicConfig(
    filename="run.log",
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(message)s",
)
log = logging.getLogger("variant_qc")
log.info("Starting run with %d samples", len(samples))
```

### Make Reruns Cheap With Checkpoints

A checkpoint is an intermediate output written under a stable name, paired with the convention that a step skips itself when its output already exists. Steps written this way are idempotent, meaning rerunning them changes nothing the second time. The payoff is re-entrancy, the ability to resume a workflow where it stopped and re-execute only the affected parts. The RiboViz project moved its analysis out of a single Python script and into a [workflow management](/knowledge/bioinformatics/snakemake-vs-nextflow-workflow-architectures) system specifically to gain incremental build and re-entrancy [9]. Inside plain Python, a done-marker file next to each output captures most of the benefit. When sample counts, retries, or cluster scheduling outgrow that pattern, hand orchestration to a workflow engine rather than growing your own.

Safety note: never write outputs over your only copy of raw data. Keep raw sequencing files read-only and write derived files to separate directories, so a path typo costs a rerun instead of a sample.

### Test With Small Known Inputs

Keep a fixture, a tiny input with a hand-verified answer, such as a dozen sequencing reads or a five-gene count table. Run it after every change. Add assertions for invariants the output must satisfy. Interactive exploration belongs in notebooks, and PaDuA builds its proteomics workflows inside Jupyter Notebooks to serve both end users and developers [10], but final analyses should run as scripts from a clean environment so the code you tested is the code that produced the results.

## A Biopython Error-Handling Walkthrough

Biopython is the standard Python library for sequence and structure work, and this section doubles as a short Biopython tutorial focused on failure modes. Install it into the environment that actually runs your scripts, then confirm with `python -c "import Bio"` and `pip show biopython`. A `ModuleNotFoundError` for the `Bio` module almost always means the install landed in a different environment than the interpreter that ran the script.

Three gotchas account for most Biopython surprises. First, `SeqIO.parse` returns a generator, so nothing is read from disk until you iterate. An empty or unreadable file can produce zero records and no error at call time, and downstream code then happily analyzes nothing. Always count records and treat an unexpectedly low count as an error. Second, format names are lowercase strings such as `fasta`, `fastq`, and `genbank`, and a wrong name raises `ValueError` at iteration time rather than at the parse call. Third, the PDB structure parser was designed for real-world mess: it handles disordered atoms and residues, insertion codes, and multiple models in one file, and it performs sanity checking to detect obvious errors [11]. That sanity checking catches malformed files, not wrong experiments, so verify chain and residue expectations in your own code as well.

A compact validator shows the pattern:

```python
from pathlib import Path
from Bio import SeqIO

def validate_fasta(path, min_records=1):
    path = Path(path)
    if not path.is_file():
        raise FileNotFoundError(f"No FASTA at {path.resolve()}")
    allowed = set("ACGTNRYKMSWBDHV-")
    count = 0
    for record in SeqIO.parse(path, "fasta"):
        count += 1
        bad = set(str(record.seq).upper()) - allowed
        if bad:
            print(f"Record {record.id}: unexpected symbols {sorted(bad)}")
    if count < min_records:
        raise ValueError(
            f"{path.name} yielded {count} records, expected at least {min_records}"
        )
    return count
```

The same shape applies to every format `SeqIO` reads: check the file, iterate, count, and fail loudly on surprises. When writing outputs, use `with` blocks so file buffers flush and close, because a crash mid-write otherwise leaves a truncated output that downstream tools may accept as complete.

## Handling Errors Across Multi-Step Pipelines

Scale changes the failure model. When one script feeds another, an error's cost multiplies through every downstream step, and partial outputs become hazards of their own. Three practices keep long chains trustworthy.

Exit codes that tell the truth. An exit code is the numeric status a program returns to its caller, with zero meaning success and anything nonzero signaling failure. Schedulers and workflow engines act on that signal. A script that catches every exception and exits zero corrupts the bookkeeping of the entire run, so let genuine failures propagate as nonzero exits.

Per-sample isolation. Combine the stop-or-continue policy from the solutions section with the logging habit, and one bad sample no longer destroys twelve hours of queue time. The failed-items file doubles as a rerun list.

Structured outputs and provenance. Write intermediates with predictable names and record what produced them, so a suspicious result can be traced back to its inputs and parameters.

When orchestration outgrows `try/except`, move it to a workflow management system, software that represents, executes, and manages multistep analyses with retries, logging, and portability handled for you. The RiboViz authors evaluated candidate systems by prototyping their real workflow before committing to one [9]. Design for both interaction modes while you are at it: automation platforms increasingly pair a graphical interface for routine runs with a scripting interface for customization, as SerialFIB does for automated cryo-FIB preparation protocols [12], and a pipeline that supports both is easier to debug interactively and safer to run unattended.

Safety note: scripts that process human genomic or clinical data handle identifiable health information. Keep those files on access-controlled systems, follow your institution's data governance policy, and keep identifiers out of logs, notebooks, and version control.

## Common Mistakes and Limitations

These are the errors that show up repeatedly in reviewed scripts, including my own.

Swallowing errors with a bare `except` and a zero exit. This hides bugs from you and lies to the pipeline. Catch specific exceptions and exit nonzero when something failed.

Debugging by rerun. Rerunning until a flaky failure disappears hides a race condition or a resource limit. Capture the conditions that trigger the failure instead.

Hardcoded absolute paths and usernames. They work on one machine and nowhere else, which blocks exactly the collaborators who could review the work.

Trusting extensions and headers. A file named `.fastq.gz` can be plain text, and the first line of a table is not always a header. Verify bytes and structure before parsing.

Mixing coordinate conventions in one codebase. Pick a convention per module, convert at the edges, and assert interval boundaries so a shift fails fast.

Testing only on the file that worked. Real collections are heterogeneous. The ChiLCV curation effort processed hundreds of genomes drawn from public databases [3], and variety on that scale guarantees edge cases your first file did not contain.

One limitation deserves plain language. Syntax-level checks cannot catch swapped sample labels, incorrect metadata, or biologically wrong but well-formed input. Those faults need metadata review against laboratory records, and no amount of exception handling substitutes for them. The practices in this article reduce engineering failures, not scientific ones.

## Prevention Practices for Reliable Scripts

Prevention costs less than diagnosis, and six habits cover most of it.

Pin your environment and record package versions in the run log so a failure can be reproduced on another machine. Freeze environments with `conda env export` or `pip freeze` and store the output beside the code.

Validate at every boundary and fail with actionable messages that name file, record, value, and expectation.

Log context at startup and per item during runs, so a failure three weeks later still carries its own explanation.

Write intermediates and done-markers so reruns are cheap and partial failures are recoverable.

Keep functions small, because short functions produce tracebacks that point precisely at the fault instead of somewhere nearby.

Maintain the fixture with a known answer and run it before every long job. A thirty-second smoke test protects hours of compute, and a colleague's review of coordinate and strand logic protects the result itself.

## Frequently Asked Questions

### What should I check first when a bioinformatics Python script fails?

Read the last line of the traceback. It names the exception type and message, which narrows the fault to a missing file, a malformed record, or a bad value within seconds. Then move upward to the first traceback frame that names your own script, because that is usually where bad data entered.

### Why does my script work on one FASTQ file but crash on another?

The second file violates an assumption the first happened to satisfy. Common culprits are truncated records that break the four-line FASTQ structure, Windows carriage returns, and gzipped files opened as plain text. Validate record structure and report the position of the first bad record.

### Should a pipeline stop when a single sample fails?

Decide per step. Stop for failures that invalidate the whole run, such as a missing reference genome. For many independent samples, log the failure, record the sample in a failed-items file, continue, and exit with a nonzero code at the end if anything failed.

### How do I handle missing values in a gene count table?

Choose a policy per column, drop, fill, or fail, and enforce it right after loading. pandas turns columns with unrecognized placeholder strings into a generic text type and lets NaN propagate silently through statistics, so assert column types and missing-value counts at load time.

### How much try/except is too much?

Wrap the smallest operation that can fail and catch only the exceptions it can raise. Handlers belong at boundaries such as file reads and tool calls. A single try/except wrapped around the entire script hides bugs instead of handling them.

### My script runs without errors but gives wrong results. What now?

Run it on a small input with a hand-verified answer. Then check the classic silent faults: coordinate conventions, strand handling, NaN propagation, and inferred column types. Structured outputs such as arrays and tables make anomalies easier to spot than printed text.

### Do I need a workflow manager instead of try/except?

They solve different layers. try/except handles failures inside one step. A workflow manager handles ordering, retries, and resuming across steps. Start with plain Python and honest exit codes, and adopt a workflow manager when sample counts, reruns, or cluster scheduling outgrow it.

### How do I write error messages collaborators can act on?

Name the file, the record, the offending value, and the expectation in one line. A message like Record KX123456 has sequence length 0, expected at least 1 gives a reader everything needed to fix the input without reading your code. Vague messages send them digging through your script instead.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "What should I check first when a bioinformatics Python script fails?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Read the last line of the traceback. It names the exception type and message, which narrows the fault to a missing file, a malformed record, or a bad value within seconds. Then move upward to the first traceback frame that names your own script, because that is usually where bad data entered."
      }
    },
    {
      "@type": "Question",
      "name": "Why does my script work on one FASTQ file but crash on another?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "The second file violates an assumption the first happened to satisfy. Common culprits are truncated records that break the four-line FASTQ structure, Windows carriage returns, and gzipped files opened as plain text. Validate record structure and report the position of the first bad record."
      }
    },
    {
      "@type": "Question",
      "name": "Should a pipeline stop when a single sample fails?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Decide per step. Stop for failures that invalidate the whole run, such as a missing reference genome. For many independent samples, log the failure, record the sample in a failed-items file, continue, and exit with a nonzero code at the end if anything failed."
      }
    },
    {
      "@type": "Question",
      "name": "How do I handle missing values in a gene count table?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Choose a policy per column, drop, fill, or fail, and enforce it right after loading. pandas turns columns with unrecognized placeholder strings into a generic text type and lets NaN propagate silently through statistics, so assert column types and missing-value counts at load time."
      }
    },
    {
      "@type": "Question",
      "name": "How much try/except is too much?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Wrap the smallest operation that can fail and catch only the exceptions it can raise. Handlers belong at boundaries such as file reads and tool calls. A single try/except wrapped around the entire script hides bugs instead of handling them."
      }
    },
    {
      "@type": "Question",
      "name": "My script runs without errors but gives wrong results. What now?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Run it on a small input with a hand-verified answer. Then check the classic silent faults: coordinate conventions, strand handling, NaN propagation, and inferred column types. Structured outputs such as arrays and tables make anomalies easier to spot than printed text."
      }
    },
    {
      "@type": "Question",
      "name": "Do I need a workflow manager instead of try/except?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "They solve different layers. try/except handles failures inside one step. A workflow manager handles ordering, retries, and resuming across steps. Start with plain Python and honest exit codes, and adopt a workflow manager when sample counts, reruns, or cluster scheduling outgrow it."
      }
    },
    {
      "@type": "Question",
      "name": "How do I write error messages collaborators can act on?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Name the file, the record, the offending value, and the expectation in one line. A message such as Record KX123456 has sequence length 0, expected at least 1 gives a reader everything needed to fix the input without reading your code. Vague messages send them digging through your script instead."
      }
    }
  ]
}
</script>

## Sources

1. ['PePApipe': A complete bioinformatics analysis pipeline for African Swine Fever Virus genome.](https://pubmed.ncbi.nlm.nih.gov/42721146/)
2. [PyMACS: A python-based automation suite for GROMACS molecular dynamics setup, simulation, and analysis.](https://pubmed.ncbi.nlm.nih.gov/42241776/)
3. [A python based automated computational framework to classify and comparative genomics analysis of the global diversity of chili leaf curl virus (ChiLCV) strains to understand virus host interactions.](https://pubmed.ncbi.nlm.nih.gov/42701968/)
4. [SNPio: a Python interface for population genomic data processing.](https://pubmed.ncbi.nlm.nih.gov/42399766/)
5. [Glucostats: an efficient Python library for glucose time series feature extraction and visual analysis.](https://pubmed.ncbi.nlm.nih.gov/40993509/)
6. [PyGOLD: a python based API for docking based virtual screening workflow generation.](https://pubmed.ncbi.nlm.nih.gov/28398502/)
7. [Automatically detecting workflows in PubChem.](https://pubmed.ncbi.nlm.nih.gov/22693105/)
8. [pyKVFinder: an efficient and integrable Python package for biomolecular cavity detection and characterization in data science.](https://pubmed.ncbi.nlm.nih.gov/34930115/)
9. [Using prototyping to choose a bioinformatics workflow management system.](https://pubmed.ncbi.nlm.nih.gov/33630841/)
10. [PaDuA: A Python Library for High-Throughput (Phospho)proteomics Data Analysis.](https://pubmed.ncbi.nlm.nih.gov/30525654/)
11. [PDB file parser and structure class implemented in Python.](https://pubmed.ncbi.nlm.nih.gov/14630660/)
12. [A modular platform for automated cryo-FIB workflows.](https://pubmed.ncbi.nlm.nih.gov/34951584/)