Skip to content

Commit 3aab516

Browse files
committed
Proposed fixes
1 parent dfbd51b commit 3aab516

6 files changed

Lines changed: 194 additions & 156 deletions

File tree

CRITICAL_BUGS.txt

Lines changed: 7 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
Critical Logic Bugs Found During Grails 7 Migration Review
22
===========================================================
33

4-
FIXED ISSUES
5-
-------------
4+
This file contains only CRITICAL bugs: those that cause crashes, data corruption,
5+
prevent app startup, or completely break major features. Non-critical bugs and
6+
Groovy 4 compatibility issues are documented in separate files.
7+
8+
CRITICAL BUGS (CAUSES CRASHES, DATA LOSS, OR WON'T START)
9+
---------------------------------------------------------
610

711
1. MISSING RETURN AFTER ERROR RENDER (all controllers) [FIXED]
812
~100 methods across all controllers sent an error response but kept
@@ -11,12 +15,6 @@ FIXED ISSUES
1115
even after a permission error was already sent back. Server crashes from
1216
trying to send two responses.
1317

14-
2. Bare `render jsonObject` without `as JSON` [FIXED]
15-
~56 render calls across 4 controllers sent JSON objects without setting
16-
the content type.
17-
CONSEQUENCE: Clients receive JSON data labeled as HTML. Browser-side
18-
JSON parsing fails, breaking the annotation editor UI.
19-
2018
3. PreferenceService NPEs on lines 161, 243, 298-299, 373-374 [FIXED]
2119
Accessed properties on possibly-null preference objects.
2220
CONSEQUENCE: Server crash (500 error) when a user has no saved
@@ -43,23 +41,12 @@ FIXED ISSUES
4341
Set/Map operations on FeatureProperty objects silently malfunction
4442
(contains() always returns false, remove() never matches, etc.).
4543

46-
8. AnnotatorController variant method duplication [FIXED]
47-
7 copy-pasted methods collapsed into a shared helper.
48-
CONSEQUENCE: No runtime bug — just maintenance risk where a fix to
49-
one copy wouldn't be applied to the other six.
50-
5144
9. PreferenceService empty list .first() calls (lines 196, 698, 709) [FIXED]
5245
Calls .first() on lists that may be empty.
5346
CONSEQUENCE: Server crash during login or organism-switching when
5447
the database has no sequences or organisms. Common during initial
5548
setup — the app becomes unusable until data is loaded.
5649

57-
10. Quartz scheduling replaced with Spring @Scheduled [FIXED]
58-
CleanupPreferencesJob used the Quartz plugin, which has no Grails 7
59-
compatible version.
60-
CONSEQUENCE: Without this fix, preference cleanup never runs, and
61-
stale preferences accumulate indefinitely in the database.
62-
6350
11. FeatureService.groovy NPE risks [FIXED]
6451
- Line 803: Assumes a transcript always has a feature location.
6552
- Lines 1528-1529: Assumes CV terms always exist in the database.
@@ -78,29 +65,6 @@ FIXED ISSUES
7865
500 instead of a helpful error message.
7966
FIX: Added null checks with error logging and skip/continue.
8067

81-
13. PermissionService.groovy dead code (line 420-421) [FIXED]
82-
The fallback branch passes null as a query parameter, so the query
83-
always returns nothing.
84-
CONSEQUENCE: No user-visible bug — the fallback on line 429 handles
85-
it. But a wasted database query runs on every permission check where
86-
no sequence name is provided. Minor performance cost.
87-
FIX: Removed the dead query; branch now directly sets sequence = null,
88-
falling through to the existing fallback on line 429.
89-
90-
14. AnnotationEditorController.getInformation permission mismatch (line 301) [FIXED]
91-
Checks WRITE permission without the request context.
92-
CONSEQUENCE: May check permissions against the wrong organism.
93-
Could show or hide feature information incorrectly.
94-
FIX: Changed checkPermissions(PermissionEnum.WRITE) to
95-
checkPermissions(inputObject, PermissionEnum.WRITE).
96-
97-
15. GroupController.updateGroup sends no response on success (line 246-281) [FIXED]
98-
Only the error path sends a response. Success sends nothing.
99-
CONSEQUENCE: Client receives an empty response after a successful
100-
group update. If the client expects JSON confirmation, it shows an
101-
error despite the operation succeeding.
102-
FIX: Added `render new JSONObject() as JSON` after successful save.
103-
10468
17. Groovy 4 log.error() type mismatch (multiple files) [FIXED]
10569
`log.error(e)` / `log.error(map)` / `log.error(e.printStackTrace())`
10670
pass non-String arguments to SLF4J Logger.error().
@@ -110,11 +74,6 @@ FIXED ISSUES
11074
GroupController, VcfController.
11175
FIX: Changed to `log.error(e.message, e)`.
11276

113-
18. Groovy 4 e.printStackTrace() calls (13 instances, 11 files) [FIXED]
114-
Prints to stderr instead of the logging framework.
115-
CONSEQUENCE: Error output lost in production (no log file capture).
116-
FIX: Replaced all 13 instances with `log.error(e.message, e)`.
117-
11877
19. Groovy 4 Date.minus() removed (FeatureEventController line 216) [FIXED]
11978
`today.minus(20*365)` used a method removed in Groovy 4.
12079
CONSEQUENCE: Feature event history queries crash on startup.
@@ -147,7 +106,7 @@ FIXED ISSUES
147106
simplified 4 repeated if-blocks into a loop.
148107

149108
23. SequenceController.setCurrentSequence null binding + session NPE [FIXED]
150-
Two issues:
109+
Three issues:
151110
a) Method signature `setCurrentSequence(Sequence sequenceInstance)`
152111
relies on Grails 7 domain class data binding, which fails when no
153112
ID is in the URL path. API callers sending `sequenceName` in JSON
@@ -193,28 +152,6 @@ FIXED ISSUES
193152
cannot start at all with a PostgreSQL database.
194153
FIX: Added `failOnError: true, flush: true` and null checks.
195154

196-
28. PermissionService.getOrganismsWithMinimumPermission/getOrganismsWithPermission
197-
wrong user (lines 101, 128) [FIXED]
198-
Both methods accept a `User user` parameter but call
199-
`getOrganismPermissionsForUser(organism, currentUser)` instead of
200-
using the passed-in `user`. `currentUser` resolves to `getCurrentUser()`
201-
which returns whoever is currently logged in.
202-
CONSEQUENCE: When checking permissions for a different user (e.g. admin
203-
viewing another user's organism list), the check runs against the admin's
204-
own permissions instead. Could show incorrect organism lists in the
205-
admin user management UI.
206-
FIX: Changed `currentUser` to `user` on both lines.
207-
208-
29. PreferenceService.evaluateSaves dead branch (lines 418-422) [FIXED]
209-
Both branches of the `if (onlySaveToken && ...)` / `else` call
210-
`evaluateSave()` with identical arguments. The `onlySaveToken` filter
211-
has no effect — all tokens are always saved regardless.
212-
CONSEQUENCE: When `onlySaveToken` is set (intended to save only one
213-
specific client token's preferences), all tokens are saved instead.
214-
Minor — causes unnecessary database writes but no data corruption.
215-
FIX: Changed to `if (!onlySaveToken || onlySaveToken == ...)` with
216-
a single `evaluateSave()` call, so non-matching tokens are skipped.
217-
218155
30. FeatureService.convertFeatureToJSONLite wrong variable in owner loop
219156
(line 2042) [FIXED]
220157
Loop iterates `for (owner in gsolFeature.owners)` but body uses
@@ -224,13 +161,6 @@ FIXED ISSUES
224161
If gsolFeature.owner is null but gsolFeature.owners is non-empty, NPE.
225162
FIX: Changed to `owner.username`.
226163

227-
31. AnnotationEditorController.getUserPermission dead code (lines 85-88) [FIXED]
228-
Built a permissions HashMap with one entry, then immediately overwrote
229-
it with `permissionService.getPermissionsForUser(user)`.
230-
CONSEQUENCE: No runtime bug — 4 lines of dead code wasting CPU on
231-
an unused organism permission lookup every time getUserPermission runs.
232-
FIX: Removed the dead lines; kept only the effective call.
233-
234164
32. @Transactional closure conversions — second pass [FIXED]
235165
Converted remaining .each{}, .collect{}, .findAll{} closures to for
236166
loops in three more files:
@@ -253,13 +183,6 @@ FIXED ISSUES
253183
added to the wrong allele. Allele updates are silently corrupted.
254184
FIX: Changed to use `oldAlleleBases` and `newAlleleBases` respectively.
255185

256-
34. VariantService.updateAlternateAlleles undefined variables in log (line 101) [FIXED]
257-
Log message referenced `${oldAltAlleleFrequency}` and `${oldProvenance}`
258-
which are never defined in the method.
259-
CONSEQUENCE: Groovy resolves undefined variables as null, so the log
260-
message prints "null" instead of useful information. Not a crash.
261-
FIX: Simplified log message to use defined variables.
262-
263186
35. VariantService.deleteAlleleInfo missing save (line 216) [FIXED]
264187
Method returns `feature` without calling `feature.save()`. Every other
265188
similar method in VariantService calls `save(flush: true, failOnError: true)`.
@@ -274,15 +197,6 @@ FIXED ISSUES
274197
no parent gene (e.g. orphaned transcripts during complex editing).
275198
FIX: Moved `gene.save()` inside the `if (gene)` block.
276199

277-
37. UserController.deleteUser render before delete (lines 558-559) [FIXED]
278-
`render jsonObject as JSON` was called before `user.delete()`. The
279-
jsonObject was built from `user.properties` which includes internal
280-
Hibernate state. After render, the user object is deleted, but the
281-
response may reference stale/detached state.
282-
CONSEQUENCE: Response may include internal Hibernate properties. If
283-
delete fails, the response already told the client it succeeded.
284-
FIX: Build a clean JSONObject, delete first, then render.
285-
286200
38. GroupController.updateGroup wrong type conversion (line 225) [FIXED]
287201
`dataObject.id.split(',').collect() as Long` casts the entire list
288202
to Long instead of converting each element.
@@ -291,12 +205,6 @@ FIXED ISSUES
291205
management UI breaks for multi-group operations.
292206
FIX: Changed to `.collect { Long.parseLong(it.trim()) }`.
293207

294-
39. GroupController.updateGroup wrong error message (line 265) [FIXED]
295-
Error message says "Failed to delete the group" in the updateGroup
296-
method. Copy-paste from deleteGroup.
297-
CONSEQUENCE: Misleading error message shown to admin users.
298-
FIX: Changed to "Failed to find the group".
299-
300208
40. AnnotatorController.updateFeature status handling (lines 257-260) [FIXED]
301209
Two bugs in status deletion:
302210
a) `Status oldStatus = data.status` — should be `feature.status` (data.status
@@ -316,58 +224,3 @@ FIXED ISSUES
316224
CONSEQUENCE: FASTA export crashes with NPE for features that have
317225
no retrievable sequence (e.g. features on unloaded sequences).
318226
FIX: Combined null and empty check: `if (seq == null || seq.length() == 0)`.
319-
320-
42. CdsService.setStopCodonReadThrough duplicate relationship (lines 120-135) [FIXED]
321-
When `replace=true`, `setChildForType()` replaces the existing
322-
relationship. But then lines 125-131 ALWAYS create a new
323-
FeatureRelationship regardless, resulting in a duplicate.
324-
CONSEQUENCE: Two identical CDS→StopCodonReadThrough relationships
325-
exist in the database. Downstream code that expects exactly one
326-
(like `hasStopCodonReadThrough()`) may behave incorrectly.
327-
FIX: If setChildForType succeeds (returns true), save and return
328-
early without creating a new relationship.
329-
330-
43. ReportService wrong domain class in HQL queries (lines 82, 84, 162, 163) [FIXED]
331-
Four HQL queries used `TransposableElement.executeQuery(...)` to run
332-
queries for `RepeatRegion` and `Exon` counts. GORM dispatches queries
333-
through the calling domain class's session context.
334-
CONSEQUENCE: RepeatRegion and Exon counts in annotator reports may
335-
return incorrect results or fail if TransposableElement's mapping
336-
doesn't cover those HQL paths.
337-
FIX: Changed to `RepeatRegion.executeQuery(...)` and
338-
`Exon.executeQuery(...)` respectively.
339-
340-
44. ReportService transcriptCount overwritten (lines 28-34) [FIXED]
341-
Lines 28-31 calculated transcriptCount from a type map, then line 34
342-
unconditionally overwrote it with `Transcript.count`. The conditional
343-
calculation was dead code.
344-
FIX: Removed the dead conditional block.
345-
346-
45. ReportService unreachable admin branch (lines 188-200) [FIXED]
347-
Inside `if (!isUserGlobalAdmin(owner))`, a nested
348-
`if (isUserGlobalAdmin(owner))` is always false. The entire
349-
Organism.listOrderByCommonName branch was dead code.
350-
FIX: Removed the dead branch, kept only the non-admin path,
351-
and converted the .each closure to a for loop.
352-
353-
46. HomeController CSV column order mismatch (line 92) [FIXED]
354-
Header said `total,count,mean,max,min,stddev` but data was written
355-
as `total,count,min,max,mean,stddev`.
356-
CONSEQUENCE: CSV downloads have swapped min/mean columns. Any
357-
pipeline parsing by column name would get wrong values.
358-
FIX: Changed header to match data order: `total,count,min,max,mean,stddev`.
359-
360-
47. ProxyController null queryString (line 62) [FIXED]
361-
`targetUrl += "?" + request.queryString` concatenates "?null" when
362-
there are no query parameters.
363-
CONSEQUENCE: Proxy requests without query params produce malformed
364-
URLs ending in "?null", which may confuse downstream services.
365-
FIX: Added null check on request.queryString before appending.
366-
367-
REMAINING ISSUES
368-
-----------------
369-
370-
16. IOServiceController.write() cleanup after error render (line 256-258)
371-
After the catch block renders an error, code continues to run
372-
outputFile.deleteOnExit(). This is intentional cleanup, not a bug —
373-
but noted for completeness.

0 commit comments

Comments
 (0)