From iris-dev
Provides few-shot examples to fix 6 common ObjectScript bugs: Quit vs Return in loops, HTML escaping order, SQL date filters, list operations, postfix conditions. Uses Bug Pattern → Root Cause → Fix structure.
npx claudepluginhub intersystems-community/iris-devThis skill uses the workspace's default tool permissions.
**HOW TO USE THESE**: Before generating any fix, first identify which pattern matches
Mandates invoking relevant skills via tools before any response in coding sessions. Covers access, priorities, and adaptations for Claude Code, Copilot CLI, Gemini CLI.
Share bugs, ideas, or general feedback.
HOW TO USE THESE: Before generating any fix, first identify which pattern matches the bug, state the root cause explicitly, THEN generate the fix. Research shows this "identify-then-fix" order reduces errors by ~15%.
BUGGY CODE:
ClassMethod IsNameUnique(name As %String, existingNames As %ListOfDataTypes) As %Boolean
{
For i=1:1:existingNames.Count() {
If (existingNames.GetAt(i) = name) {
Quit 1
}
}
Quit 0
}
BUG PATTERN: Quit value inside a For loop exits the loop, not the method.
ROOT CAUSE: After Quit 1, execution continues AFTER the loop — the method returns
the value of Quit 0 at the bottom, or empty string if Quit had a value (IRIS-specific).
The return values are also logically swapped (found = not unique = should return 0).
FIXED CODE:
ClassMethod IsNameUnique(name As %String, existingNames As %ListOfDataTypes) As %Boolean
{
For i=1:1:existingNames.Count() {
If (existingNames.GetAt(i) = name) {
Return 0
}
}
Return 1
}
KEY RULE: Return value always exits the method. Quit value inside a loop only exits the loop.
BUGGY CODE:
ClassMethod DisplayMessage(message As %String) As %String
{
Set safe = $REPLACE(message, "<", "<")
Set safe = $REPLACE(safe, ">", ">")
Set safe = $REPLACE(safe, "&", "&")
Return "<div>" _ safe _ "</div>"
}
BUG PATTERN: Wrong escaping order causes double-encoding.
ROOT CAUSE: Escaping < first produces <. Then escaping & turns < into
&lt; — the ampersand in the entity gets escaped again.
FIXED CODE:
ClassMethod DisplayMessage(message As %String) As %String
{
Set safe = $REPLACE(message, "&", "&")
Set safe = $REPLACE(safe, "<", "<")
Set safe = $REPLACE(safe, ">", ">")
Return "<div>" _ safe _ "</div>"
}
KEY RULE: Always escape & FIRST, then <, then >.
BUGGY CODE:
ClassMethod SearchItems(category As %String) As %ListOfDataTypes
{
Set results = ##class(%ListOfDataTypes).%New()
Set stmt = ##class(%SQL.Statement).%New()
Set sc = stmt.%Prepare("SELECT Name FROM Catalog.Item WHERE Category = ?")
Set rs = stmt.%Execute(category)
While rs.%Next() { Do results.Insert(rs.%Get("Name")) }
Return results
}
BUG PATTERN: Query missing filter to exclude expired records.
ROOT CAUSE: No ActiveUntil date check — returns both active and expired items.
ActiveUntil = "" (NULL) means never expires; ActiveUntil < today means expired.
FIXED CODE:
ClassMethod SearchItems(category As %String) As %ListOfDataTypes
{
Set results = ##class(%ListOfDataTypes).%New()
Set stmt = ##class(%SQL.Statement).%New()
Set sc = stmt.%Prepare("SELECT Name FROM Catalog.Item WHERE Category = ? AND (ActiveUntil IS NULL OR ActiveUntil >= ?)")
Set rs = stmt.%Execute(category, +$HOROLOG)
While rs.%Next() { Do results.Insert(rs.%Get("Name")) }
Return results
}
KEY RULE: +$HOROLOG = today as integer. Pattern: AND (field IS NULL OR field >= ?).
BUGGY CODE:
ClassMethod ProcessItems(items As %ListOfDataTypes) As %Integer
{
For i=1:1:items.Count() {
If (items.GetAt(i) [ "DELETE") { Do items.RemoveAt(i) }
}
Return items.Count()
}
BUG PATTERN: Removing items during forward iteration causes index skipping.
ROOT CAUSE: After RemoveAt(2), the old item 3 becomes item 2 — but i advances to 3,
skipping the newly-shifted item.
FIXED CODE:
ClassMethod ProcessItems(items As %ListOfDataTypes) As %Integer
{
For i=items.Count():-1:1 {
If (items.GetAt(i) [ "DELETE") { Do items.RemoveAt(i) }
}
Return items.Count()
}
KEY RULE: Iterate backwards (Count():-1:1) when removing items in-place.
BUGGY CODE:
ClassMethod GetPatientAge(id As %String) As %Integer
{
Set patient = ##class(Patient).%OpenId(id)
Set birthYear = $PIECE(patient.DOB, "-", 1)
Return +$EXTRACT($ZDATE($HOROLOG, 3), 1, 4) - birthYear
}
BUG PATTERN: No null check after %OpenId — crashes when record not found.
ROOT CAUSE: %OpenId returns "" when ID doesn't exist. Accessing .DOB on ""
causes <INVALID OREF> runtime error.
FIXED CODE:
ClassMethod GetPatientAge(id As %String) As %Integer
{
Set patient = ##class(Patient).%OpenId(id)
If '$IsObject(patient) { Return -1 }
Set birthYear = $PIECE(patient.DOB, "-", 1)
Return +$EXTRACT($ZDATE($HOROLOG, 3), 1, 4) - birthYear
}
KEY RULE: Always If '$IsObject(obj) { Return <default> } immediately after %OpenId.
BUGGY CODE (causes #5559 parse error):
For {
Set key = $ORDER(users(key))
Quit:key = ""
If ($ZCONVERT(key, "U") = searchName) { Return users(key) }
}
BUG PATTERN: Spaces in postfix condition cause parse error.
ROOT CAUSE: Quit:key = "" — the space before = causes IRIS UDL parser error #5559.
Postfix conditions must be written without any spaces.
FIXED CODE:
For {
Set key = $ORDER(users(key))
Quit:key=""
If ($ZCONVERT(key, "U") = searchName) { Return users(key) }
}
KEY RULE: Postfix conditions: Quit:condition — NO spaces anywhere in the condition.
Quit:key="" not Quit:key = "". Also never append postfix to other statements.
BUGGY CODE:
ClassMethod BuildCSV(values As %ListOfDataTypes) As %String
{
Set result = ""
For i=1:1:values.Count() {
If (i > 1) { Set result = result _ "," }
Set result = result _ values.GetAt(i)
}
Return result
}
BUG PATTERN: O(n²) string concatenation — fails performance test for large lists.
ROOT CAUSE: Each result _ value creates a new string copying all previous data.
For 1000 items this copies ~500,000 total characters.
FIXED CODE:
ClassMethod BuildCSV(values As %ListOfDataTypes) As %String
{
Set lst = ""
For i=1:1:values.Count() {
Set lst = lst _ $LISTBUILD(values.GetAt(i))
}
Return $LISTTOSTRING(lst, ",")
}
KEY RULE: Accumulate into $LIST then convert once with $LISTTOSTRING(lst, ",").
For every bug fix request:
This structure is mandatory — stating the pattern before generating code prevents the most common errors.