21 KiB
title, task, lineage_type, upstream_source, upstream_sha, imported_at, prompt_class, upstream_changes, author, validated
| title | task | lineage_type | upstream_source | upstream_sha | imported_at | prompt_class | upstream_changes | author | validated |
|---|---|---|---|---|---|---|---|---|---|
| Bug Patterns Reference | import | https://github.com/mims-harvard/ToolUniverse/blob/e2520a96/skills/devtu-self-evolve/references/bug-patterns.md | e2520a96 | 2026-06-26 | prompt | accepted | upstream | false |
Bug Patterns Reference
Detailed code-level fix patterns discovered through role-play debug rounds.
Table of Contents
Rounds 52–57 (core patterns)
- Regex Overmatch: Mutation vs Fusion
- Silent Parameter Alias
- Always-Fires Conditional
- Silent Normalization
- Notation / Case Sensitivity
- Truncation Not Top-Level
- Gene Symbol Substring Match
- try/except Indentation
- Multi-Word Search Hints
- Expression Units Inference
- Auto-Selected vs Explicit Study
- CIViC Therapy + AND Logic
Rounds 68–78 (API-specific patterns)
- DGIdb Client-Side Filtering
- CPIC PostgREST Filter Prefix
- CPIC Recommendation Join
- KEGG Organism-Specific Gene Search
- HPA Deprecated Column
- PDBePISA Operation from Config
- GTEx Dataset Selection
- GxA Silent geneId Ignore
- ExpressionAtlas Schema Key Mismatch
- Proteins API Human Priority
- RCSB Null Type in Schema
- ClinVar Search Field Tags
- ENCODE Tissue+Organism Filter
- GEO Double-Adding Search Terms
- CPIC Warfarin Algorithm Guideline
- PharmGKB clinpgxid vs pharmgkbid
- ENCODE ChIP-seq Assay Title
- RegulomeDB Parameter Names
- BindingDB API Typo
- ProteomeXchange Title and Instruments
- MetabolomicsWorkbench moverz URL
- MetaboLights Pagination Ignored
- ChEBI Search Nulls and HTML
- HMDB No Open API
- PharmGKB relatedGenes Filter
Regex Overmatch: Mutation vs Fusion
Tools: civic_search_evidence_items, civic_search_variants Bug IDs: BUG-56A-001
A regex that normalizes GENE1-GENE2 fusion notation can accidentally match GENE-MutationCode (e.g., EGFR-T790M, BRAF-V600E, KRAS-G12C) because mutation codes start with an uppercase letter.
Fix: Use a lambda that checks if the second part matches HGVS protein-change format before applying the substitution:
def _maybe_fuse(m):
second = m.group(2)
# Protein-change: single letter + digits + letter/asterisk (e.g. T790M, V600E, G12C)
if re.match(r"^[A-Z]\d+[A-Z*]?$", second):
return m.group(0) # leave unchanged
return m.group(1) + "::" + second
normalized = re.sub(r"\b([A-Z][A-Z0-9]*)-([A-Z][A-Z0-9]+)\b", _maybe_fuse, mol_profile)
When the input matches the mutation pattern and returns 0 results, add a hint:
elif re.search(r"\b[A-Z][A-Z0-9]*-[A-Z]\d+[A-Z*]?\b", mol_profile):
space_form = mol_profile.replace("-", " ", 1)
mp_warn += f" If '{mol_profile}' is a point mutation, try molecular_profile='{space_form}'..."
Silent Parameter Alias
Tools: SYNERGxDB_search_combos Bug IDs: BUG-55A-011
Users pass cancer_type but tool only accepts sample, tissue_name, tissue. Parameter silently dropped → all-tissue results returned with status: "success".
Fix:
sample = (
arguments.get("sample")
or arguments.get("tissue_name")
or arguments.get("tissue")
or arguments.get("cancer_type") # ADD aliases here
)
Always-Fires Conditional
Tools: GtoPdb_get_interactions Bug IDs: BUG-55A-002, BUG-55A-003, BUG-55B-001
item.get("approved") on interaction records returns None always (field only exists on /ligands objects). So has_approved was always False → conditional note always fired. Also used wrong category language ("kinase/enzyme") for GPCR targets.
Fix: Remove the conditional check; always emit the note as factual guidance. Use neutral language; embed the queried gene symbol:
if isinstance(data, list) and len(data) > 0:
_chembl_target = gene_symbol or result.get("queried_target", {}).get("name", "the target")
result["coverage_note"] = (
"GtoPdb interactions list pharmacological research compounds — approved "
"drugs for this target may not be represented. For approved drugs and "
"clinical compounds, use ChEMBL_get_drug_mechanisms or "
f"ChEMBL_search_molecules with target_name='{_chembl_target}'."
)
Silent Normalization
Tools: civic_search_evidence_items, civic_search_variants Bug IDs: BUG-55A-008, BUG-55B-005
CIViC normalizes therapy (lowercase → Title Case) and molecular_profile (BCR-ABL1 → BCR::ABL1) without disclosing the transformation. Users debugging empty results can't see what was tried.
Fix: Track pre-normalization values, add a normalization_note field to the result:
_therapy_normalized_from = None
_mp_normalized_from = None
# When normalizing therapy:
_therapy_normalized_from = therapy
arguments["therapy"] = therapy.title()
# After result is built:
_norm_parts = []
if _therapy_normalized_from:
_norm_parts.append(f"therapy '{_therapy_normalized_from}' → '{arguments.get('therapy')}' (CIViC uses Title Case)")
if _mp_normalized_from:
_norm_parts.append(f"molecular_profile '{_mp_normalized_from}' → '{arguments.get('molecular_profile')}' ...")
if _norm_parts:
result["normalization_note"] = "Input auto-normalized: " + "; ".join(_norm_parts) + "."
Notation / Case Sensitivity
Tools: civic_search_evidence_items Bug IDs: BUG-55B-005 (fusion), BUG-53B-002 (therapy case)
CIViC uses :: for gene fusions (BCR::ABL1 Fusion, EML4::ALK Fusion). Users write hyphenated forms (BCR-ABL1) → 0 results.
CIViC therapy names are Title Case ("Sotorasib" not "sotorasib").
Fix (fusion normalization):
import re as _re
mol_profile = arguments.get("molecular_profile")
if mol_profile and isinstance(mol_profile, str):
normalized_mp = _re.sub(
r"\b([A-Z][A-Z0-9]*)-([A-Z][A-Z0-9]+)\b",
r"\1::\2",
mol_profile,
)
if normalized_mp != mol_profile:
_mp_normalized_from = mol_profile
arguments = dict(arguments)
arguments["molecular_profile"] = normalized_mp
Fix (therapy case):
if therapy == therapy.lower() or therapy == therapy.upper():
_therapy_normalized_from = therapy
arguments["therapy"] = therapy.title()
Truncation Not Top-Level
Tools: CancerPrognosis_get_gene_expression Bug IDs: BUG-55A-006
Truncation info (e.g., 500 of 1100 samples returned) buried inside data.note string. Researchers reading top-level fields miss it.
Fix: Add top-level flags:
response = {"status": "success", "data": result_data}
if len(values) > max_samples:
response["truncated"] = True
response["truncation_note"] = (
f"Returning {len(values[:max_samples])} of {len(values)} samples. "
f"Pass max_samples={len(values)} (up to 2000) to retrieve the full dataset."
)
return response
Gene Symbol Substring Match
Tools: GtoPdb_get_interactions Bug IDs: BUG-54B-001
?name=AR returns all targets containing "AR" (13+ targets). ?geneSymbol=AR is the unambiguous HGNC lookup.
Fix: Try ?geneSymbol= first; fall back to ?name= if nothing returned:
gs_url = f"{base_url}/targets?{urlencode({'geneSymbol': gene_symbol})}"
gs_resp = request_with_retry(...)
if gs_resp.status_code == 200:
gs_targets = gs_resp.json()
if gs_targets:
target_id = gs_targets[0]["targetId"]
# Fall back to ?name= only if geneSymbol returned nothing
if target_id is None:
lookup_url = f"{base_url}/targets?{urlencode({'name': gene_symbol})}"
...
try/except Indentation
Every try: MUST have an except at the exact same indentation level. A common error is placing code after try: at the same level as the try: keyword — Python sees the try block as having no except.
# CORRECT
try: # 16 spaces
resp = ... # 20 spaces (inside try)
if resp.ok: # 20 spaces (still inside try)
...
except Exception: # 16 spaces — matches try
pass
# WRONG — SyntaxError
try: # 16 spaces
resp = ... # 20 spaces
if resp.ok: # 16 spaces — OUTSIDE try, before except!
...
except Exception: # 16 spaces — Python: "try without except"
pass
Multi-Word Search Hints
Tools: GtoPdb_search_targets, GtoPdb_search_ligands Bug IDs: BUG-54B-002
name="androgen receptor" returns empty — GtoPdb text search doesn't reliably match multi-word phrases.
Fix: When count=0 and query has a space, add a hint suggesting first word only:
if result["count"] == 0 and name_q and " " in str(name_q):
first_word = str(name_q).split()[0]
result["multi_word_hint"] = (
f"GtoPdb text search may not match multi-word phrases like '{name_q}'. "
f"Try a single keyword: name='{first_word}'."
)
Expression Units Inference
Tools: CancerPrognosis_get_gene_expression Bug IDs: BUG-54A-002
_get_expression_units(profile_id) infers units from the profile ID string. Some studies use misleading suffixes (e.g., aml_ohsu_2022 uses _rna_seq_v2_mrna but stores log2 RPKM).
Fix: Return (profile_id, profile_name) from _get_mrna_profile() and prefer the actual API description over inference:
def _get_expression_units(self, profile_id, profile_name=None):
if profile_name:
return profile_name # Prefer actual API name
# ... fallback inference from profile_id
Auto-Selected vs Explicit Study
Tools: CancerPrognosis_get_gene_expression Bug IDs: BUG-54A-004
study_note always said "Auto-selected study_id=..." even when user explicitly passed a full study ID like brca_tcga.
Fix: Detect explicit specification and use a different message:
study_was_explicit = (
cancer == study_id_arg
and study_id_arg
and "_" in str(study_id_arg)
and str(study_id_arg).upper() not in TCGA_STUDY_MAP
and str(study_id_arg).upper() not in _CANCER_NAME_ALIASES
)
"study_note": (
f"Using explicitly specified study_id='{study_id}'."
if study_was_explicit
else f"Auto-selected study_id='{study_id}'. Use CancerPrognosis_search_studies "
"to find alternative cohorts for this cancer type."
),
CIViC Therapy + AND Logic
Tools: civic_search_evidence_items, civic_search_variants Bug IDs: BUG-54A-001, BUG-54A-005
- When both
queryandvariant_nameare provided, one silently wins. Fix: apply AND logic. - When
molecular_profile + therapyreturns 0, silently fails. Fix: auto-probe without therapy to surface available therapy names.
Fix (AND logic):
raw_query = arguments.get("query")
raw_variant_name = arguments.get("variant_name") or arguments.get("variant")
_both = raw_query and raw_variant_name and raw_query != raw_variant_name
if _both:
query_term = raw_query
_secondary_term = raw_variant_name
else:
query_term = raw_query or raw_variant_name
_secondary_term = None
# Later, apply AND filter:
if _secondary_term:
filtered = [v for v in filtered if _secondary_term.lower() in v.get("name","").lower()]
result["filter_note"] = f"Applied AND logic for query+variant_name."
Fix (therapy auto-probe):
if mol_profile and therapy and not disease and len(evidence_nodes) == 0:
probe_args = {k: v for k, v in arguments.items() if k != "therapy"}
probe_nodes = civic_query(probe_args)
available_therapies = sorted({
t.get("name") for node in probe_nodes
for t in node.get("therapies", []) if t.get("name")
})
result["therapy_warning"] = (
f"No evidence for '{mol_profile}' + therapy='{therapy}'. "
f"Available therapies: {available_therapies[:10]}"
)
DGIdb Client-Side Filtering (Feature-68A-001/002)
Tools: DGIdb_get_drug_gene_interactions
Pattern: API ignores interaction_types and sources filters server-side → returns everything.
Fix: Fetch all, filter client-side:
if interaction_types:
results = [r for r in results if r.get("interactionTypes") and
any(it in interaction_types for it in r["interactionTypes"])]
if sources:
results = [r for r in results if r.get("source") in sources]
# Always wrap with status
return {"status": "success", "count": len(results), "data": results}
CPIC PostgREST Filter Prefix (Feature-68A-004)
Tools: CPICSearchPairsTool
Pattern: PostgREST requires equality filter as eq.VALUE, not bare VALUE.
Fix: Auto-prepend eq. if not already present:
def _postgrest_eq(value):
v = str(value)
return v if v.startswith("eq.") else f"eq.{v}"
params["genesymbol"] = _postgrest_eq(gene_symbol)
CPIC Recommendation Join (Feature-68A-006)
Tools: CPIC_get_recommendations
Pattern: /recommendation endpoint doesn't include drug name — need to join /drug table.
Fix: Use ?select=*,drug(name) in the URL query string for PostgREST joins.
KEGG Organism-Specific Gene Search (Feature-68B-001)
Tools: kegg_find_genes
Pattern: /find/genes/{keyword} ignores organism param → returns all organisms.
Fix: Use /find/{organism}/{keyword}:
url = f"https://rest.kegg.jp/find/{organism}/{keyword}" if organism else f"https://rest.kegg.jp/find/genes/{keyword}"
HPA Deprecated Column (Feature-68B-002)
Tools: HPA_get_protein_expression
Pattern: ppi column was deprecated; API returns error listing alternatives (enhanced, supported, approved, uncertain).
Fix: Remove ppi from select list; return error message with alternatives when API responds with column-not-found error.
PDBePISA Operation from Config (Feature-68B-006)
Tools: PDBePISA tools
Pattern: operation param exposed as public parameter → users pass wrong values.
Fix: Read operation from the tool's JSON fields config, never expose as public argument.
GTEx Dataset Selection (Feature-69A-001/002)
Tools: GTEx_get_median_gene_expression
Pattern: gtex_v10 returns empty for medianGeneExpression endpoint; only gtex_v8 works reliably.
Fix: Always default to gtex_v8. Add note in result:
dataset = arguments.get("dataset", "gtex_v8")
if dataset == "gtex_v10":
result["dataset_note"] = "gtex_v10 may return empty for this endpoint; gtex_v8 is recommended."
GxA Silent geneId Ignore (Feature-69A-003)
Tools: ExpressionAtlas_get_experiment
Pattern: /experiments/{accession} API ignores geneId parameter silently.
Fix: Apply geneId filter client-side on the returned data.
ExpressionAtlas Schema Key Mismatch (Feature-69A-004)
Tools: ExpressionAtlas tools
Pattern: return_schema key experiments but code returns baseline_experiments.
Fix: Make key in return_schema match exactly what the code produces. Use baseline_experiments.
Proteins API Human Priority (Feature-69A-007)
Tools: proteins_api_search Pattern: Gene/name searches return non-human results first.
Fix: Add taxid=9606 default for gene and name searches to prioritize human entries.
RCSB Null Type in Schema (Feature-69B-002/010)
Tools: RCSB tools
Pattern: return_schema has type: null which is invalid JSON Schema.
Fix: Use ["array", "null"] or ["string", "null"] for nullable types. Also avoid using protein-only PDB IDs as test examples — use IDs that represent the tested operation.
ClinVar Search Field Tags (Feature-70B-004/005)
Tools: ClinVar tools
Pattern: [variant_id] is not a valid eSearch field → use [uid]. [disease/phenotype] not valid → use bare condition text (no brackets).
Fix:
# Wrong:
query = f"{variant_id}[variant_id]"
query = f"{condition}[disease/phenotype]"
# Right:
query = f"{variant_id}[uid]"
query = condition # bare text, NCBI searches all fields
ENCODE Tissue+Organism Filter (Feature-70A-003)
Tools: ENCODE_search_experiments Pattern: Combining tissue + organism filter → HTTP 404.
Fix: Fall back without organism filter when 404 occurs:
try:
resp = requests.get(url_with_organism)
if resp.status_code == 404:
resp = requests.get(url_without_organism)
except Exception:
...
GEO Double-Adding Search Terms (Feature-70A-008)
Tools: geo_search_datasets Pattern: Code adds "methylation" to query even when it's already present.
Fix: Check before appending:
if "methylation" not in query.lower():
query += " methylation"
CPIC Warfarin Algorithm Guideline (Feature-70B-001)
Tools: CPIC_get_recommendations
Pattern: Warfarin guideline (ID 100425) uses algorithm endpoint, not /recommendation. Returns 0 rows from recommendation endpoint.
Fix: Detect warfarin (or any guideline that uses algorithm-based dosing) and redirect to /algorithm endpoint or explain in the response.
PharmGKB clinpgxid vs pharmgkbid (Feature-70B-002/011)
Tools: PharmGKB tools
Pattern: CPIC list_guidelines returns clinpgxid, but code uses pharmgkbid → 404 from PharmGKB.
Fix: Use clinpgxid from CPIC response when building PharmGKB URLs.
ENCODE ChIP-seq Assay Title (Feature-73B)
Tools: ENCODE_search_experiments
Pattern: Querying assay_title=ChIP-seq returns 0; correct value is TF ChIP-seq.
Also: organism filter uses wrong nested field name.
Fix: Map user-friendly aliases to ENCODE-specific values:
ASSAY_ALIASES = {"ChIP-seq": "TF ChIP-seq", "CHIP": "TF ChIP-seq"}
assay_title = ASSAY_ALIASES.get(assay_title, assay_title)
RegulomeDB Parameter Names (Feature-73B)
Tools: RegulomeDB tools
Pattern: assembly=hg19 is wrong → use genome=GRCh38. Test example rs123456 doesn't exist → use rs4994.
Fix: Update parameter names and test examples to match actual API.
BindingDB API Typo (Feature-73A)
Tools: BindingDB tools
Pattern: API endpoint has typo: getLindsByXxx (not getLigandsByXxx). Also response key is bdb.affinities not affinities.
Fix: Use exact typo as in the real API URL. Access response["bdb.affinities"].
ProteomeXchange Title and Instruments (Feature-73A)
Tools: ProteomeXchange tools
Pattern: title field is plain string not dict. instruments use {name, accession} not nested terms.
Fix: Access dataset.get("title") directly. For instruments: inst.get("name") not inst["terms"][0]["name"].
MetabolomicsWorkbench moverz URL (Feature-73A/74A)
Tools: MetabolomicsWorkbench tools
Pattern: moverz endpoint needs database prefix: /moverz/{database}/{mz}/{adduct}/{tolerance}. Also exactmass endpoint is broken → use moverz/REFMET/{mass}/M/{tolerance}.
Fix:
url = f"https://www.metabolomicsworkbench.org/rest/moverz/REFMET/{mz}/M+H/{tolerance}"
MetaboLights Pagination Ignored (Feature-74A)
Tools: MetaboLights tools
Pattern: /ws/studies API silently ignores size and page params → always returns full list.
Fix: Apply pagination client-side after fetching all results:
all_studies = resp.json()
start = page * size
return all_studies[start:start + size]
ChEBI Search Nulls and HTML (Feature-74A)
Tools: ChEBI_search_compounds
Pattern: smiles and inchikey always null from search API. Names contain HTML entities (&, <).
Fix: Strip HTML from names using re.sub(r"<[^>]+>", "", name) and html.unescape(name). Don't return null smiles/inchikey — omit them or fetch from detail endpoint.
HMDB No Open API (Feature-74A)
Tools: HMDB tools Pattern: HMDB has no open REST API; tools were returning stub success responses.
Fix: Return {"status": "error", "message": "HMDB does not provide a public REST API. Use MetabolomicsWorkbench or ChEBI instead."}.
PharmGKB relatedGenes Filter (Feature-74B)
Tools: PharmGKB tools
Pattern: relatedGenes.id and relatedGenes.symbol filters not supported by API — silently returns all results.
Fix: Return informative message: "relatedGenes filter is not supported by PharmGKB API. Filter results client-side or use gene-specific endpoints instead."