CNC M98, M99 & M198 TROUBLESHOOTING BIBLE 2026
FANUC and Haas Subprogram Errors, Program Not Found, Infinite Loops, Repeat Counts, P/L Syntax, External Program Calls and CNC Debugging
The Complete Practical Guide to Diagnosing CNC Subprogram Problems
CNC subprograms are among the most useful features in professional CNC programming.
They allow programmers to reuse machining operations, reduce duplicate code, simplify repetitive patterns, and organize complex programs.
But subprograms also create some of the most confusing CNC programming problems.
A machine may report that a called program cannot be found.
A subprogram may run once when it should run multiple times.
A program may return to the wrong location.
A machine may appear to repeat the same operation forever.
Or a program that works on one FANUC-style control may fail on a Haas machine because the call syntax is different.
This guide focuses on the practical troubleshooting of:
- M98 subprogram calls
- M99 subprogram returns
- M198 external program calls
- Haas M97 local subprograms
- P-address program references
- L-address repeat counts
- FANUC-style packed P formats
- Nested subprograms
- Infinite loops
- Missing programs
- Modal-state problems
- Macro and subprogram interactions
- Postprocessor-related subprogram errors
Important: CNC subprogram syntax is not completely universal. The exact controller model, software version, machine-builder configuration, and installed options determine supported behavior.
The code examples below illustrate programming concepts. They are not machine-ready machining programs. Verify the relevant manufacturer documentation and use approved simulation and prove-out procedures before physical execution.
PART I — UNDERSTANDING M98, M99 AND M198
1. What Does M98 Mean in CNC?
On many FANUC-style and Haas controls, M98 calls a subprogram.
A subprogram is a separate program or program section that executes when called by another program.
Basic conceptual structure:
O1000 (MAIN)
M98 P2000
M30
Called program:
O2000 (SUBPROGRAM)
M99
The main program calls O2000.
The subprogram executes.
M99 returns execution to the caller on compatible controls.
This example has no machining movement and demonstrates call structure only.
2. What Does M99 Mean in CNC?
M99 commonly returns from a subprogram.
Its behavior depends on the execution context.
Typical use:
O2000
(SUBPROGRAM OPERATIONS)
M99
Potential problems arise when M99 appears in a main program or when the program is executed directly rather than called.
Some controls may repeat or branch instead of ending normally.
Troubleshooting rule: Always determine whether the current program is being executed as a main program or as a called subprogram.
3. What Does M198 Mean in CNC?
M198 is associated with calling external programs on certain CNC controls.
It may be used with supported external storage or program-access configurations.
However:
- Not every control supports M198.
- External storage requirements vary.
- Program numbering and call syntax vary.
- Software options may be required.
- The storage location must be available to the control.
M198 should not be treated as a universal replacement for M98.
4. What Does Haas M97 Mean?
On supported Haas controls, M97 calls a local subprogram identified by a sequence number within the same program.
Illustrative structure:
O1000
M97 P100
M30
N100
(LOCAL SUBPROGRAM)
M99
The call references the local sequence label.
This differs from the separate-program concept commonly associated with M98.
5. M97 vs M98 vs M198
| Code | Common purpose | Key consideration |
| M97 | Haas local subprogram call | Uses a local sequence reference |
| M98 | Subprogram call | Program reference syntax varies |
| M99 | Subprogram return | Context determines behavior |
| M198 | External program call | Control and storage dependent |
Never assume these commands are interchangeable.
PART II — M98 PROGRAM NOT FOUND
6. Why Does M98 Report Program Not Found?
A missing-subprogram error may occur when the control cannot resolve the requested program reference.
Possible causes:
- The target program does not exist.
- The program number is incorrect.
- The subprogram is stored in the wrong location.
- The call syntax is incorrect.
- The control is using an unexpected program directory or device.
- The program is not accessible in the current operating mode.
- The controller does not support the assumed calling convention.
Start by confirming the exact program number and storage location.
7. M98 P2000 Not Working
Example:
M98 P2000
On a compatible control, this may request program O2000.
If the call fails, check:
- Is O2000 present?
- Is the number exactly correct?
- Is the target stored where the controller searches?
- Does the controller require another format?
- Is the target program accessible?
- Is the call being interpreted as expected?
Do not immediately rename files or change controller parameters without understanding the cause.
8. Subprogram Number Mismatch
Main program:
O1000
M98 P2000
M30
Available subprogram:
O2001
M99
The call references 2000, but the available subprogram is 2001.
This simple mismatch can prevent execution.
A program-reference checker can detect it automatically.
9. Program Number vs File Name
On some CNC systems, the internal program number and external file name are related but not identical concepts.
For example, an NC file might be transferred under one file name while containing a different O-number.
Controller behavior varies.
Check both the program identity recognized by the control and the storage format.
10. Hidden Program Location Problems
A program may exist but still be unavailable to the calling operation.
Possible reasons include:
- Different memory area
- External storage not mounted
- Wrong directory
- Incorrect program search path
- Program access restrictions
- Unsupported execution mode
A file visible on a computer is not necessarily accessible to the CNC interpreter.
PART III — M98 P AND L SYNTAX
11. What Does P Mean in M98?
The P address commonly identifies the target subprogram.
Example:
M98 P2000
On compatible controls, this calls the referenced program.
But P formatting is not identical on every controller.
12. What Does L Mean in M98?
On some controllers, L specifies a repeat count.
A commonly documented Haas-style concept is:
M98 P2000 L3
This illustrates calling the specified subprogram multiple times on controls that support this syntax.
Compatibility warning: Do not assume every FANUC control supports the same L-address format.
13. FANUC Packed P Repeat Count
Some FANUC-style controls support a packed P-address convention.
Illustrative example:
M98 P00302000
Under a compatible packed format, this can represent:
- Repeat count: 0030
- Program number: 2000
However, supported digit layouts and interpretation depend on the controller.
Do not use packed P syntax without confirming the exact programming manual.
14. Why Does M98 Repeat the Wrong Number of Times?
Potential causes:
- Incorrect packed P format
- Incorrect L value
- Wrong controller dialect
- Unexpected nested calls
- Subprogram logic that changes the expected behavior
- A postprocessor using the wrong convention
The first step is to determine the syntax actually supported by the control.
15. M98 Repeat Count Is Not a Loop Safety Check
A repeat count controls the requested number of calls under the applicable syntax.
It does not automatically guarantee that the called program terminates correctly.
If the subprogram contains an unintended loop, the repeat count may never complete as expected.
PART IV — M99 INFINITE LOOP TROUBLESHOOTING
16. Why Does M99 Cause an Infinite Loop?
An apparent infinite loop can occur when program execution returns or repeats in an unexpected way.
Possible causes include:
- M99 in the wrong program context
- Main program executed with M99 instead of normal termination
- Incorrect subprogram call structure
- Unintended nested calls
- Incorrect branch target
- Controller-specific M99 behavior
The correct diagnosis depends on the exact execution path.
17. M99 vs M30
M99 is commonly used for subprogram return.
M30 commonly ends a main program.
Conceptual example:
O1000
M98 P2000
M30
O2000
M99
This is a common structural pattern on compatible controls.
But program-ending behavior remains controller-specific.
18. M99 in the Main Program
Consider:
O1000
M99
On some controls, executing this directly may cause program repetition or unexpected return behavior.
This is not equivalent to a universally valid program end.
Check the manual for the exact machine.
19. Missing M99 in a Subprogram
If a subprogram does not return as expected, execution may encounter an unintended program boundary or termination command.
Potential symptoms:
- Premature program stop
- Unexpected end
- Alarm
- Incorrect execution sequence
Inspect the complete called program.
20. M99 Return Address Problems
Some controls support optional return or branch behavior associated with M99.
Such formats are controller-specific.
If a program returns to an unexpected block, inspect the exact M99 syntax and the active call context.
PART V — NESTED SUBPROGRAM ERRORS
21. What Is a Nested Subprogram?
A nested subprogram calls another subprogram.
Example structure:
O1000 MAIN
|
+– O2000
|
+– O3000
|
+– RETURN
|
+– MAIN CONTINUES
Nested calls can simplify complex programs.
They also increase the importance of correct call and return logic.
22. Excessive Nesting
Controllers may limit subprogram nesting depth.
The permitted depth varies.
If the program exceeds the supported limit, the control may raise an alarm or reject execution.
Consult the exact controller manual rather than assuming a universal nesting limit.
23. Recursive Subprogram Calls
Consider this call graph:
O1000 -> O2000
O2000 -> O1000
Without a valid termination strategy, the calls can form recursion.
A subprogram debugger should identify this cycle.
24. Missing Nested Subprogram
A main program may successfully call O2000.
O2000 may then fail when calling O3000.
The operator may initially suspect the first M98 call.
Trace the entire call chain to identify the actual missing target.
25. Subprogram Call Graph Debugging
A practical call-graph report should include:
- Calling program
- Called program
- Call block
- Repeat count
- Nesting level
- Return location
- Missing references
- Potential cycles
This is especially valuable for large CNC programs.
PART VI — MODAL STATE AND SUBPROGRAMS
26. Why Modal State Matters
Subprograms often inherit the active modal state of the caller.
Important modes include:
- G90/G91
- G17/G18/G19
- G20/G21
- G54–G59
- G40/G41/G42
- G43/G49
- G80
- Feed mode
- Motion mode
A subprogram that assumes the wrong state can produce unexpected motion.
27. G90/G91 Subprogram Error
Suppose a subprogram switches to incremental positioning:
G91
If it returns without restoring the intended mode, the caller may continue in G91.
This can change the meaning of subsequent coordinates.
A subprogram should document its modal-state requirements and effects.
28. Work Offset Changes Inside Subprograms
A subprogram may select a different work coordinate system.
If the caller expects the original offset to remain active, subsequent movement may occur in the wrong location.
Track work offset changes across every call and return.
29. Canned Cycle Left Active
If a subprogram activates a drilling cycle and does not cancel it appropriately, subsequent coordinate blocks may trigger unintended operations.
On compatible controls, G80 commonly cancels active canned cycles.
The exact cycle behavior must be verified.
30. Tool Compensation Changes
Subprograms may alter tool length or cutter compensation.
A return to the caller does not necessarily restore every previous modal setting automatically.
This is why modal-state analysis should cross program boundaries.
PART VII — M198 EXTERNAL PROGRAM TROUBLESHOOTING
31. M198 Program Not Found
Possible causes:
- External program unavailable
- Incorrect storage device
- Wrong directory
- Incorrect program identifier
- Unsupported call format
- Missing controller option
- Storage access problem
The exact diagnosis depends on the controller’s external-program execution model.
32. M198 vs M98
M98 commonly calls a subprogram through the control’s supported program-reference mechanism.
M198 is associated with external program calling on certain controls.
However, the distinction and storage behavior are not identical across all CNC systems.
Use the machine’s official documentation.
33. External Storage Problems
External program execution may depend on:
- Supported media
- File format
- Directory structure
- Program naming
- Device availability
- Communication configuration
Do not assume that a readable storage device automatically supports executable subprogram calls.
34. M198 and Controller Options
Some functions require installed software or hardware options.
A program that works on one machine may fail on another machine with a different option set.
Check the controller’s supported feature list.
35. M198 Postprocessor Errors
A CAM postprocessor may output M198 when the target machine expects a different call method.
Check:
- Postprocessor target
- Controller configuration
- Program storage method
- External-call support
- Expected program naming
Do not replace M198 with M98 without verifying the intended execution behavior.
PART VIII — HAAS M97 TROUBLESHOOTING
36. M97 Local Program Not Found
On supported Haas controls, M97 references a local sequence number.
Possible causes:
- Missing N label
- Incorrect P value
- Wrong program structure
- Unsupported control behavior
The local label must match the call reference.
37. M97 vs M98 Confusion
M97 and M98 are not identical.
A programmer may mistakenly use a separate-program number where a local label is expected.
Identify the intended program organization before choosing the call command.
38. M97 Local Subprogram Placement
The location of the local subprogram and the main program’s termination structure matter.
A program should not accidentally fall through into a local subprogram during normal execution.
Follow the applicable Haas programming rules.
39. M97 Repeat Behavior
Repeat-count syntax and support must be verified against the specific Haas control documentation.
Do not assume that syntax from a different control applies unchanged.
40. Haas Subprogram Debugging Checklist
Confirm:
- Correct control generation
- Correct call type
- Correct program or sequence reference
- Correct repeat count
- Correct M99 return
- Correct modal state
- Correct program termination
PART IX — MACROS AND SUBPROGRAMS
41. Macro Calls vs M98 Calls
Macro calls and ordinary subprogram calls may use different commands and parameter-passing conventions.
On compatible FANUC Macro B controls, G65 is commonly associated with custom macro calls.
M98 is commonly associated with ordinary subprogram calls.
They should not be treated as interchangeable.
42. Local Variables
Some macro systems provide local variables associated with macro call context.
Their initialization, lifetime, and nesting behavior depend on the controller.
A subprogram may not receive arguments in the same way as a macro call.
43. Common Variables
Common variables may be shared across program execution contexts.
Potential problems include:
- Unexpected retained values
- Incorrect initialization
- Variable overwrites
- Conflicting subprogram assumptions
Use clear variable conventions and validate inputs.
44. Macro Loop vs M98 Repeat
A macro loop can use conditional logic.
An M98 repeat count requests repeated subprogram execution under the supported syntax.
The two mechanisms serve different purposes.
Macro loops are more flexible but require careful termination logic.
45. Macro Alarm and Subprogram Errors
A macro may stop execution when an input or calculated condition is invalid.
A programmer should distinguish:
- Missing subprogram errors
- Invalid macro expressions
- Controller-generated program alarms
- Machine-builder alarms
The exact alarm number and message must be checked against the machine documentation.
PART X — REAL-WORLD DEBUGGING SCENARIOS
46. Scenario: M98 Calls the Wrong Program
Symptom: The control executes an unexpected subprogram.
Investigate:
- P reference
- Packed repeat format
- Program numbering
- Controller syntax
- Postprocessor output
Verification: Trace the exact call target using the control’s documented format.
47. Scenario: Subprogram Runs Only Once
Symptom: A repeated operation executes once.
Investigate:
- Repeat-count syntax
- Repeat value
- Supported controller format
- Early termination
- Conditional logic
Verification: Test the call structure using a non-cutting, controller-compatible diagnostic program under approved procedures.
48. Scenario: Subprogram Never Returns
Symptom: Execution does not continue after the call.
Investigate:
- Missing M99
- Unexpected branch
- Nested call
- Macro loop
- Controller-specific return behavior
Verification: Build the execution call graph.
49. Scenario: M99 Repeats the Main Program
Symptom: The main program starts again unexpectedly.
Investigate:
- M99 used as program termination
- Direct execution of a subprogram
- Controller return semantics
- Optional branch formats
Verification: Confirm the program’s intended entry and termination points.
50. Scenario: Program Works in Simulation but Fails on Machine
Symptom: A generic simulator accepts M98, but the CNC alarms.
Investigate:
- Simulator dialect
- Controller call format
- Missing target program
- Storage restrictions
- Installed options
Verification: Use controller-aware verification and the machine’s official documentation.
51. Scenario: M198 Works on One Machine Only
Symptom: External calls work on one CNC but not another.
Investigate:
- Controller option differences
- Storage configuration
- Program naming
- Supported media
- Software version
Verification: Compare the exact machine configurations.
52. Scenario: Repeated Pattern Moves to the Wrong Location
Symptom: A repeated machining pattern appears offset or displaced.
Investigate:
- G90/G91
- Work offset changes
- Macro calculations
- Subprogram entry position
- Incremental movement assumptions
Verification: Track modal state and coordinate values across calls.
PART XI — SUBPROGRAM VERIFICATION CHECKLIST
53. Before Calling a Subprogram
Verify:
- Target exists
- Call syntax is supported
- Repeat count is correct
- Tool is correct
- Work offset is correct
- Coordinate mode is correct
- Feed mode is correct
- Entry position is understood
- Clearances are verified
54. Inside the Subprogram
Verify:
- Expected initial state
- Coordinate interpretation
- Motion sequence
- Compensation state
- Cycle state
- Variable usage
- Exit conditions
- Return command
55. After Returning
Verify:
- Expected program resumes
- Positioning mode is correct
- Work offset is correct
- Compensation state is correct
- Canned cycles are canceled as required
- No unexpected repeat remains active
- Next movement has verified clearance
56. Before Production Release
Verify:
- Exact controller compatibility.
- Complete program call graph.
- All referenced programs available.
- No unintended recursion.
- Repeat counts reviewed.
- Modal states tracked.
- Macro inputs validated.
- Final NC program simulated.
- Tool and fixture clearances checked.
- Approved machine prove-out completed.
PART XII — M98/M99 DEBUGGING TOOLS
57. Subprogram Reference Checker
A useful software checker can scan for:
- M98 references
- Referenced O numbers
- Missing targets
- Duplicate program numbers
- Potential recursion
The parser must understand the selected controller dialect.
58. Subprogram Call Graph Viewer
A call graph visualizes program relationships.
Example:
O1000
|
+– O2000
| |
| +– O3000
| +– RETURN
|
+– O4000
|
+– END
This makes complex execution paths easier to understand.
59. Modal-State Analyzer
A modal-state analyzer tracks active G-code modes.
Useful fields:
- Units
- Plane
- Absolute/incremental
- Work offset
- Tool compensation
- Canned cycle
- Feed mode
The analyzer should report changes that persist after a subprogram returns.
60. Repeat Count Calculator
A repeat-count helper should support the exact selected controller syntax.
For example, it may show:
- Target program
- Requested repetitions
- Expected total calls
- Supported P/L format
It should not apply a universal packed P interpretation to every machine.
61. CNC Program Diff
Comparing two versions can reveal:
- Changed P values
- Changed L values
- Added M98 calls
- Removed M99 returns
- Changed program numbers
- Modified macro logic
This is useful after CAM postprocessor updates.
PART XIII — FREQUENTLY ASKED QUESTIONS
62. What Is M98 in CNC?
M98 commonly calls a subprogram on compatible controls.
63. What Is M99 in CNC?
M99 commonly returns from a subprogram. Its behavior depends on execution context.
64. What Is M198 in CNC?
M198 is associated with external program calls on certain CNC controls.
65. What Is the Difference Between M97 and M98?
On supported Haas controls, M97 commonly calls a local subprogram, while M98 calls a separately referenced subprogram.
66. Why Does M98 Say Program Not Found?
Possible causes include an incorrect program number, missing target, wrong storage location, or incompatible call format.
67. Why Does M99 Loop Forever?
Possible causes include incorrect main-program termination, unexpected return behavior, recursion, or invalid call structure.
68. Can M98 Repeat a Subprogram?
Yes, many compatible controls support repeat counts, but syntax varies.
69. What Does P Mean in M98?
P commonly identifies the called program. Some formats also encode repetition information.
70. What Does L Mean in M98?
On controls supporting the format, L may specify the repeat count.
71. Can M98 Call Another M98?
Nested subprogram calls are supported on many controls, subject to controller-specific limits and rules.
72. Can M98 Call a Macro?
Ordinary subprogram calls and macro calls have different semantics. Use the command and argument conventions supported by the control.
73. Is M198 the Same as M98?
No. External program calling and ordinary subprogram calling may use different storage and execution mechanisms.
74. Does FANUC M98 Work on Haas?
Many concepts overlap, but exact syntax and repeat-count conventions must be verified.
75. Can a Subprogram Change G90 to G91?
Yes, on compatible controls. The resulting modal state can affect subsequent program execution.
76. Can a CNC Simulator Check M98/M99?
Only if it supports the selected controller dialect and can access all referenced programs.
PART XIV — QUICK TROUBLESHOOTING TABLE
| Symptom | Possible cause | First check |
| M98 program not found | Missing or incorrect target | Program reference |
| M98 wrong program | P format mismatch | Controller syntax |
| M98 runs once | Repeat format issue | Repeat count |
| M99 infinite loop | Return/termination issue | Execution context |
| M99 wrong return | Branch or call structure | Call graph |
| M97 label missing | Incorrect local reference | N label |
| M198 program not found | External storage problem | Device and path |
| Nested call alarm | Excessive nesting or recursion | Call depth |
| Wrong machining location | Modal-state mismatch | G90/G91 and offsets |
| Simulator differs from machine | Dialect or model mismatch | Controller configuration |
PART XV — PROFESSIONAL SUBPROGRAM DESIGN
77. Design for Reuse
A reusable subprogram should have a clearly defined purpose.
Avoid mixing unrelated machining operations into one large subprogram.
78. Document Entry Conditions
Every subprogram should document its expected:
- Tool
- Work offset
- Coordinate mode
- Entry position
- Feed mode
- Compensation state
This reduces ambiguity.
79. Document Exit Conditions
The caller should know which modes may change.
For example:
- Is G90 restored?
- Is the canned cycle canceled?
- Is the original work offset restored?
- Are macro variables modified?
Explicit contracts make subprograms easier to maintain.
80. Avoid Unnecessary Nesting
Nested subprograms can be useful, but excessive nesting complicates debugging.
Use the simplest structure that clearly represents the machining process.
81. Validate Repeat Counts
A repeat count should be:
- Supported by the controller
- Correctly formatted
- Within permitted limits
- Appropriate for the machining operation
An incorrect repeat count can repeat cutting movements unexpectedly.
82. Use Version Control
Record changes to:
- Main programs
- Subprograms
- Program references
- Macro logic
- Postprocessor output
A revised subprogram can affect multiple main programs.
83. Test the Complete Call Chain
Testing a subprogram alone is not always equivalent to testing it through its intended caller.
The caller may establish essential modal states and variables.
Verify the complete execution context.
84. Maintain a Program Dependency List
For each main program, record its required subprograms.
Example:
MAIN: O1000
REQUIRES:
– O2000
– O3000
– O4000
This helps prevent missing-program errors during transfer and backup.
85. Verify After Postprocessor Changes
A postprocessor update may change:
- M98 syntax
- Repeat-count format
- Program numbering
- Subprogram naming
- M99 placement
- External-call behavior
Compare the new output with previously verified programs.
PART XVI — THE FUTURE OF CNC SUBPROGRAM DEBUGGING
86. Automatic Call Graph Generation
A controller-aware tool can build a dependency graph from all available NC programs.
This can help identify missing references and potential recursion.
87. Automatic Modal-State Tracking
A debugger can record the active state before and after each subprogram.
Unexpected changes can be highlighted.
88. AI-Assisted Subprogram Explanation
AI may help explain call structures and identify suspicious patterns.
However, AI must not invent missing controller behavior or assume that unsupported syntax is valid.
89. Controller-Aware Subprogram Simulation
A robust simulator should account for:
- M97
- M98
- M99
- M198
- Controller-specific P/L conventions
- Nested calls
- Macro variables
- External program availability
Unsupported behavior should be reported explicitly.
90. The Ideal CNC Subprogram Troubleshooting Workflow
IDENTIFY THE CONTROLLER
↓
READ THE EXACT ALARM
↓
LOCATE THE CALL BLOCK
↓
VERIFY THE TARGET PROGRAM
↓
CHECK P/L SYNTAX
↓
TRACE THE CALL GRAPH
↓
CHECK M99 RETURN BEHAVIOR
↓
TRACK MODAL STATES
↓
VALIDATE MACRO VARIABLES
↓
VERIFY THE FINAL NC PROGRAM
↓
PERFORM APPROVED PROVE-OUT
CONCLUSION — MASTER M98, M99 AND M198 TROUBLESHOOTING
Subprograms make CNC programming more efficient, but they require precise control of program references, execution flow, repeat counts, and modal states.
Most subprogram troubleshooting begins with a few essential questions:
Which controller is running the program?
Which program is calling which target?
Does the target exist and is it accessible?
Is the P/L syntax correct for this control?
Does M99 return as intended?
Are nested calls and loops properly structured?
Does the subprogram preserve the caller’s required modal state?
The answers often reveal whether the problem is a missing file, incorrect syntax, unexpected execution context, or deeper programming issue.
THE PROFESSIONAL RULE:
TRACE THE CALL. VERIFY THE TARGET. CHECK THE RETURN. CONFIRM THE MODAL STATE.
2026 Edition — M98 CNC, M99 M-Code, M198 External Calls, Haas M97, FANUC Subprograms, Repeat Counts, Program Not Found Errors and CNC Subprogram Debugging.
Leave a comment