Resolving Omron CQM1 Compile Subroutine Not Closed Error

James Nishida11 min read
OmronOther TopicTroubleshooting
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

1. Overview

The Omron CQM1 and CQM1H series programmable controllers, programmed with CX-Programmer (or legacy Syswin / SYSMAC-CPT), occasionally return a compile-time diagnostic indicating that a defined subroutine is never closed by a return instruction. The failure signature is consistent: a subroutine opened with the SBN(92) ladder instruction has no matching RET(93) rung before the program section terminator, so the offline compiler flags the entire block as un-terminated and refuses to assemble it for download.

This article walks through the diagnostic path, the underlying root cause as described in the official CQM1H Series Programming Manual (W364), and a reproducible field-procedure for restoring a clean compile. The same fix applies to both standalone CQM1 CPUs and CQM1H inner boards mounted into the SRM1 or compact modular backplanes, because the cyclic task structure and instruction semantics are shared.

Field caveat: CQM1 firmware cannot self-detect this fault at runtime the way a CJ2 does. The fault is reported only at compile time inside CX-Programmer. Operators sometimes discover the problem only when a planned online edit is blocked by an outstanding compile failure.

2. Problem Symptom and Error Identification

When you select Program > Compile (or press Ctrl+F7) inside a CQM1 project, the Output Window at the bottom of CX-Programmer populates with warnings and errors. The symptom that distinguishes the subroutine-not-closed fault is a message of the form:

ERROR: Sub #SBN_xx is not closed. (Rung nnnn, Section <section_name>)

Typical error characteristics observed in the field:

  • The error references a rung number that is past the end of any visible RET in the section.
  • Double-clicking the error jumps the editor to the offending rung - usually a rung immediately after the last visible RET(93), near the physical end of the program section.
  • The same section contains valid SBS(91) calls from cyclic scan code, but the ladder body that follows the corresponding SBN(92) lacks a closing RET(93).
  • Compile fails with return code 0xFFFFFFFF in the build log; no .OBJ or .HEX file is regenerated in the project folder.
  • If the user ignores the error and forces an online change, the PLC will reject the transfer with AR2301 (SYSTEM ERROR - PROGRAM ERR) on power-up, requiring a fresh download.

The error count and message format depend on the CX-Programmer version. CX-Programmer 7.x and later display the message in the Output Window with hyperlink-style navigation; CX-Programmer 6.0 and Syswin display it inside the Build Output tab with no click-through, requiring the engineer to scroll the section manually.

3. Root Cause Analysis

The CQM1 instruction set follows a strict subroutine-call frame structure:

  • SBS(91) N — Subroutine Call from the cyclic ladder.
  • SBN(92) N — Subroutine Define, marks the entry point of subroutine N.
  • RET(93) — Return from subroutine. Mandatory between SBN(92) and the next SBN(92) or END(01).
  • END(01) — End of cyclic scan / end of program section.

Per the CQM1H Programming Manual (W364), Section 6 (Subroutine Programming), the compiler requires that every SBN(92) be paired with exactly one RET(93) within the same program section. RET(93) may appear anywhere in the body of the subroutine (early-return patterns are supported), but it must appear at least once.

The root cause for the reported fault is one of the following:

  1. Missing RET — The engineer deleted or copied-out the closing RET(93) rung when refactoring, leaving an open subroutine frame. Compilers read the program linearly: when they encounter SBN(92) N, they push N onto an open-frame stack. END(01) flushes the stack. If the stack is non-empty at flush time, the compiler reports N as "not closed".
  2. Migrated / pasted-in content — Code moved from another project or another Omron family (e.g., CP1E) does not always carry RET(93) with it, especially when copied block-by-block.
  3. Section boundaries — CX-Programmer permits subroutines to span sections, but it does not auto-insert a return at the section footer. The engineer is responsible for closing the subroutine before starting a new section.

Stack analysis performed by the CQM1H firmware simulator shows that the error message points to the offending SBN(92) (or the section-end that triggered the flush), not to the missing RET rung itself - which is why double-click navigation ends "after" the last visible RET in many cases.

4. Affected Hardware, Firmware, and Software Versions

Component Family / Catalog Behavior
CPU Unit CQM1-CPU11 / CPU21 / CPU41 / CPU42 / CPU43 / CPU44 / CPU45 All CPU revisions; SBN/RET frame applies.
CPU Unit CQM1H-CPU11 / CPU21 / CPU51 / CPU61 Same frame rules. Inner-board versions affected identically.
Programming Software CX-Programmer 1.x → 9.x Compile fault appears in all versions; navigation behavior differs.
Legacy Support Tool Syswin 3.x, SYSMAC-CPT Same fault, displayed as UNCLOSED SBN in the build log.
Instruction Set Reference W364 Chapters 4, 6 SBN(92), RET(93), SBS(91) spec sheets.
Operation Reference W226 Section 4 / Section 6 Status indicator and program mode behavior.

Per the CQM1 Operation Manual (W226), Section 4 (Indicators), the CPU module enters PROGRAM mode and stops the controlled process when an unterminated subroutine survives a transfer; the ERR LED on the front face lights steady red with RDY off. Always isolate the controlled equipment before performing the corrective edit.

5. Resolution Procedure (Step-by-Step)

Use the following procedure for a single-subroutine fix. The procedure is valid for both offline and online-edit modes; for online edits on a running process, schedule the change during a scheduled maintenance window.

  1. Open the project in CX-Programmer. Confirm the target PLC family is CQM1 or CQM1H under PLC > Change PLC Type.
  2. Select Program > Compile (Ctrl+F7) and note every error in the Output Window. Record the rung number, section name, and subroutine index reported.
  3. Double-click each error in the Output Window. The editor jumps to the rung immediately past the suspected close point.
  4. Identify the open SBN(92) — scroll upward from the cursor position until you locate the SBN(92) N rung that the error references. Note its subroutine number.
  5. Insert a new rung at the logical end of the subroutine body (just before the next SBN(92) or END(01)) using Insert > Rung.
  6. Place RET(93) on the new rung. The rung should contain the RET(93) instruction alone, optionally followed by the comment // end sub N for future maintenance.
  7. Recompile the project. The Output Window should clear the original error. Other unrelated errors may remain; address each in turn.
  8. Save the project to disk (File > Save) and verify the timestamp on the .CXP file.
  9. Transfer to PLC only after every compile error is resolved. Confirm the download finishes with status Complete; abort if FAILED is reported.
  10. Cycle the mode switch from PROGRAM back to RUN/MONITOR and observe the RDY LED recover.
Safety check: If the controlled machine has a maintenance mode that bypasses PLC control during programming, enable it now. An open subroutine downloaded accidentally can force the scan to execute past an intended termination point.

6. Alternative Resolution: Section Restructure

When multiple subroutine frames are tangled or the section has been heavily edited, restructuring the project sections is faster than inserting Rets one at a time. The CQM1H allows subroutines to live in dedicated sections:

  1. Insert a new program section (Insert > Section) named Sub_NN where NN is the orphan subroutine number.
  2. Cut the offending SBN(92) NN rung and the entire subroutine body from the current section. Paste into the new section, immediately under a freshly inserted SBN(92) NN rung.
  3. Append RET(93) to the bottom of the new section, then END(01) as required.
  4. Replace the original rung with a single SBS(91) NN call from the cyclic ladder so call sites remain unchanged.
  5. Recompile and verify the section boundaries in the project tree. CQM1 task structure requires that all subroutines reachable from the cyclic scan be defined in the same PLC project; cross-project SBS calls are not supported on CQM1.

This pattern is the cleanest fix when the original section has grown into a mixed-purpose body that executes logic, defines subroutines, and contains indirect block moves. It also prevents the same root cause from reappearing on a future copy/paste.

7. Verification Steps

After applying either fix, run the verification sequence below before bringing the process back online.

  • Compile clean — Output Window reports 0 errors, 0 warnings for the entire project.
  • Cross-reference scan — In View > Cross Reference, confirm every SBS(91) N resolves to an SBN(92) N defined in the project, and every SBN(92) N has exactly one RET(93).
  • Offline simulation — Run CX-Simulator (or CX-Programmer step monitor) with a representative I/O stimulus. Step through the subroutine to confirm that execution returns to the call site at the RET rung rather than falling through to the next SBN.
  • Online comparePLC > Compare with PLC should report identical ladder with no procedure-level delta.
  • LED status — The CPU RDY LED is solid green in RUN, ERR LED is off. Refer to the indicator summary in W226 Section 4.

8. CQM1 Subroutine Instruction Reference

The following table summarises the subroutine-related instructions applicable to the CQM1 and CQM1H, sourced from W364, Chapter 4 (Instruction Set).

Mnemonic Function Code Operands Purpose
SBS 91 N (0–255) Subroutine Call. Decrement the SP, push return address, jump to SBN(N).
SBN 92 N (0–255) Subroutine Define. Mark the entry point for subroutine N. Must be paired with a RET.
RET 93 None Subroutine Return. Pop the stack and return to the caller.
END 01 None End of program. Flushes any unresolved SBN frame; this is where the diagnostic is raised.
MC / MCR 19 / 20 N (0–255) Master Control / Master Control Reset; does not affect SBN/RET semantics.
SBN2 / RET2 92/93 (variant) N Available on selected CQM1H revisions for nested subroutines in inner boards; same RET semantics apply.

The CQM1 stack is 8 levels deep; nested subroutine calls beyond 8 report STACK OVERFLOW in the CQM1 Operation Manual (W226) indicator table. The SBN/RET pairing rule is independent of nesting depth - every opened subroutine must close with a RET regardless of nesting.

9. Program Section Best Practices

Adopt these practices to prevent the same fault in future revisions. They are derived directly from the structuring guidance in W364 and extended from field experience.

  • One subroutine per section. When a section is dedicated to a single subroutine index, the section footer carries the lone RET(93) automatically in your muscle memory.
  • Use templates. Create a CX-Programmer section template named SUB_TEMPLATE containing SBN(92) N at the top and RET(93) at the bottom. Duplicate the template when adding new subroutines.
  • Avoid paste-without-RET. Bulk copy/paste from CP1E or CJ2 projects often drops bare rung logic. Always verify SBN/RET pairs after any copy-paste operation across families.
  • Run a build hook. Configure CX-Programmer to run an offline compile at save time so the diagnostic surfaces immediately rather than at transfer time.
  • Maintain a comment line above every SBN and RET stating the subroutine index. CX-Programmer navigates faster when errors reference indices that match the comment text.
  • Lint with a custom mnemonic search for SBN(92) in Find > Find in Program and visually confirm each SBN has a vertically aligned RET.

10. Common Compile Error Patterns and Cross-Reference

The subroutine-not-closed fault is one of several related compile diagnostics in the CQM1 family. The matrix below maps the most common patterns so you can recognise variants in the field.

Symptom in Output Window Likely Cause Fix
Sub #SBN_xx is not closed Missing RET(93) for xx Insert RET(93) before next SBN or END.
Sub #SBN_xx already defined Duplicate SBN(92) xx in same project Renumber one of the duplicates.
Undefined subroutine #SBN_xx SBS(91) xx has no matching SBN(92) xx Add the SBN rung or remove the SBS call.
Subroutine stack overflow Nested >8 levels (CPU limit) Flatten nested calls or refactor.
SBN not at start of section Code inserted above SBN(92) Move SBN(92) to top rung of section.
END missing in section Section cut off mid-subroutine Append END(01) to the section.

11. Diagnostics Matrix

Use this matrix when the reported error is ambiguous, or when the fix in Sections 5 or 6 does not clear the build log.

Indicator Expected After Fix If Not - Check
Output Window errors 0 Hidden tabs in CX-Programmer; collapse them.
RDY LED Solid green Mode switch in PROGRAM; PLC battery alarm A001 active.
ERR LED Off AR2301 / AR2400 series errors; check W364 Appendix.
Cross Reference for SBS=NN One match to SBN=NN Multiple or zero matches indicate duplicate calls.
Online step monitor RET returns to caller Falling through suggests a misplaced JMP/BSET.
Compare with PLC Identical Delta on a SBN/RET rung means partial download.

For deeper indicator interpretation, the CQM1 Operation Manual (W226) lists every LED combination with the corresponding PC Setup bit or auxiliary relay. Cross-check the RDY / ERR / IN1 triplet listed in the manual against the panel indicator after each fix attempt.

12. FAQ

Why does CX-Programmer report "subroutine #41 not closed" when the error points at #39 in the source code?

The compiler flag is generated when it encounters END(01) or a section break with an un-paired frame on its stack. If nested subroutines 39 and 41 are both open and only one closes, the message references the deepest open frame (41). Close 41 first, recompile, and the secondary 39 case often resolves automatically because the closing sequence was previously disabled.

Can a subroutine span multiple sections in CX-Programmer?

Structurally yes. The CQM1 does not enforce section boundaries, but CX-Programmer does not auto-insert a RET(93) at the section footer. Always close the subroutine explicitly with RET(93) before ending any section.

Will the CQM1 CPU report this fault at runtime if the project is downloaded?

No. Compile-time only. The fault is caught in CX-Programmer before transfer. If you bypass the offline build (some legacy flash utilities) the PLC will refuse to enter RUN and stop with ERR lit; revert to PROGRAM, reload via CX-Programmer, and bring it back to RUN.

Is there a quick navigation shortcut to find orphan RETs?

Use Find > Find Instruction (Ctrl+Shift+F), search for RET(93), and visually count occurrences against SBN(92) matches in the same section. A standalone SBN(92) without a matching RET(93) is the orphan; place a RET(93) on the rung immediately above END(01).

Does migrating subroutines from a CP1E or CJ2 cause this fault on CQM1?

Yes, frequently. The instruction codes for SBN/RET are identical across Omron families, but copied subroutines occasionally lose the closing rung or drag incompatible task-control rungs. Always run a full compile after any cross-family migration before transferring to the CQM1 CPU.

Back to blog