REXX Tips & Tricks:Troubleshooting

From EDM2
Jump to: navigation, search

his section contains hints for some REXX errors, symptoms, and messages. Note that a lot of hints in this section are captured from CompuServe messages.

In addition to the tips mentioned below I suggest everyone having problems with REXX programs should get the new Object-Oriented REXX. Object-Oriented REXX has much more descriptive error messages and the documentation is better than the normal REXX online documentation.

Another good source for Troubleshooting information is the Common REXX Pitfalls page on the home page of Quercus Systems (see Internet - Web Pages)

Note that the information related to various APARs is included in the sections General hints for REXX, REXXUTIL functions and Change the WPS with REXX. See those sections for additional information.

Here's a list of further sections in REXX Tips & Tricks with information about various errors:

Errors in OS/2 commands
General REXX Errors
Object REXX Errors
Errors in REXXUTIL functions
WPS related Errors

No light, no sound

Problem
The call of a REXX program neither produces the expected result nor an error message.
Hint
Check if the file REX.MSG (normally in the directory C:\OS2\SYSTEM) is in a directory included in the environment variable DPATH. This file contains the error messages for the REXX Interpreter.

RC = 1041 or RC = 1002

Problem
The call of an OS/2 program leads to the error code SYS1041 or SYS1002, although the called OS/2 program is available through the environment variable PATH.
Hint
Check the value of the environment variable COMSPEC.

It runs, runs not, runs, runs not, ...

Problem
A REXX program works sometimes and sometimes not.
Hint
If the program uses file I/O, check if you're closing all opened files. The number of file handles is limited in an OS/2 session. (see MaxFH on how to increase the number of file handles; see lsof for getting a list of all open files)

Please note that the entry FILES=nn in the file CONFIG.SYS is only valid for DOS sessions!

What the hell is he doing?

Problem
The results of a REXX program aren't as expected.
Hint
Turn the NOVALUE condition on. (see SIGNAL)

Haeaeh - Syntax error?

Problem
The execution of a REXX program leads to mysterious syntax errors.
Hint
Check the file for unbalanced comment chars. A simple way to do that is: Use the find-and-replace-function of your editor to replace the string "/*" with "/*" and the string "*/" with "*/". The number of changes for both calls should be equal. If not, there's an unbalanced comment in your file.
Another method to detect unbalanced comment chars: Use an editor with syntax highlighting.

See also: Using comments

2 + 2 = 5

Problem
The results of mathematical expressions aren't as expected.
Hint
Change the NUMERIC settings to a greater value.

DLL loading failed

Problem
The call of RxFuncAdd failed although the DLL to load is accessible through the LIBPATH.
Hint
For some DLLs the name of the DLL function to register (this is the third parameter for RxFuncAdd) is case-sensitive! This depends on how the DLL was linked.
Use an ExeHdr tool or take a look at the DLL with a Hex-viewer to get the proper spelling for the name of the register function.
Another cause for this error:
Maybe the DLL needs some other DLLs - these DLLs must also be accessible through the LIBPATH.
To check which other DLLs the DLL need, you can use the excellent program PMDLL. or IBM's chkdll32.exe.

See also: The REXX API functions

SYS1801

Problem
The execution of a REXX program leads to the error SYS1801 although REXX support is installed.
Hint
Maybe the EAs are corrupted. Try a CHKDSK /F on the drive where the REXX program resides.

REX0003: Program is unreadable

Problem
Calling a REXX cmd leads to the error message Program is unreadable.
Hint
This error is generated if a CMD file called with the CALL statement is locked or deleted by another process. You'll get this error also if the function name for a CALL statement is a directory name or a device name (e.g. call con, see Reserved directory & file names). The above is also true for function calls. (see also #REXX programs without an extension and #Error: Failure during initialization)

1 +++ @REXX g + ? OS / 2 REXXSAA 6.00 12 Jul 1995

Problem
The call of a REXX cmd leads to an error message like
    1 +++   @REXX g + ? OS / 2 REXXSAA 6.00 12 Jul 1995 ( P ? + §? ??
REX0013: Error 13 running E:\DATEN\DFUE\DOWNLOAD\TEST\OREXXTRY.CMD, line 1:
Invalid character in program
Hint
You tried to run a tokenized Object-Oriented REXX program under the normal REXX interpreter. You need Object-Oriented REXX to run this program (see also #"Compiling" REXX programs and #Error: Failure during initialization)

Error: Failure during initialization

Problem
The call of an EXE program containing REXX code leads to the error message
REX0003E: Error 3: Failure during initialization
REX0200E: Error 3.1: Failure during initialization: File "%1" is unreadable
Hint
You tried to run a tokenized Classic REXX program under Object-Oriented REXX. Object-Oriented REXX cannot execute token images of REXX programs created with Classic REXX. EXEs with token images are created by some of the GUI environments for REXX or by #RxCLS - a REXX 'compiler' ($). You may try to load the Classic REXX interpreter prior to calling the REXX program causing this error (see #Using Classic REXX if Object REXX is the default REXX; see also #REX0003: Program is unreadable)

SYS0008

Problem
There seems to be a bug in REXX which leads to the error SYS0008 if you repeatedly call an OS/2 command in a REXX program. (see the #Example for the SYS0008 error)
Hint
To avoid this problem use equivalent REXX functions where possible. If there's no such REXX function, you can create a new session every # calls. (see the #Workaround for the SYS0008 error). Note that this is only a workaround - not a solution!

(see also #SYS0008 - 2 -)

Example for the SYS0008 error

/* example program to generate a SYS0008 error                       */
/* This program generates a SYS0008 error after about 90 rounds if   */
/* the file TATA.TXT does not exist                                  */
/* see also the Workaround                                           */

  "C:"
  "CD \"
  "MD C:\TOTO"
  "MD C:\TOTO\TOTO"
  "MD C:\TOTO\TOTO\TOTO"
  "CD C:\TOTO\TOTO\TOTO"
  CHEMIN = "TATA\TATA\TATA.TXT"
  do i = 1 to 100

                    /* this DIR command causes the SYS0008 error     */
                    /* after about 40 to 80 rounds                   */
    "DIR "CHEMIN
    if RC = 0 then
      say "OK"
    else
      say i
  end /* do i = 1 to 100 */
exit

Workaround for the SYS0008 error

 
/* workaround for the SYS0008 error                                   */

                    /* get the current round count and session no.    */
  parse arg round sessionNo

  if round = "" then
    round = 1           /* initial value is 1                         */

  if sessionNo = "" then
    sessionNo = 1       /* initial value is 1                         */

                        /* get the name of this program               */
  parse source . . thisProg

  if sessionNo = 1 then
  do
                        /* do initialisations only once               */
    "C:"
    "CD \"
    "MD C:\TOTO"
    "MD C:\TOTO\TOTO"
    "MD C:\TOTO\TOTO\TOTO"
    "CD C:\TOTO\TOTO\TOTO"
  end /* if sessionNo = 1 then */

  CHEMIN = "TATA\TATA\TATA.TXT"

                    /* this is an endless loop!                       */
  do i = round
    if i // 40 = 0 then
    do
                    /* start a new session every 40 rounds            */
                    /* You may use additional parameters or a private */
                    /* queue to pass global variables to the next     */
                    /* pass                                           */
      "start /c /f " thisProg  i+1  sessionNo+1

                    /* close this session                             */
      "exit"
    end /* if i // 40 = 0 then */

                    /* execute the needed OS/2 commands               */
    "DIR "CHEMIN ">NUL"

                    /* process the results here                       */
    say "session: " || sessionNo || ", round " || i || " --> RC = "rc
  end /* do i = round */

exit

SYS0008 - 2 -

Problem
Passing a null string as a command (which is not a valid command) to the CMD interpreter will return the SYS0008 error message after the 32nd iteration.

Example:

/* example for the SYS0008 bug */

 do i=1 to 33

   /* the next stmt returns a */
   /* null string to the CMD  */
   /* which leads to the      */
   /* SYS0008 error after the */
   /* 32nd execution.         */

   Beep(220,250)

   say "i is " || i || ", rc is " || RC
 end /* do i=1 to 33 */

Note that you can replace the statement Beep(220,250) with every other statement returning a null string - even with a function defined in your REXX program!

This seems to be an OS/2 interpreter bug. Note that 4OS2 does handle this situation correctly.

Hint
Do not use a function returning a null string this way (see also SYS0008).

This bug seems to be fixed in WARP 4 Fixpak #5.

REXX programs without an extension

Problem
The REXX interpreter executes an external procedure from a file with any or without an extension and/or without the leading comment normally necessary for REXX programs.
Hint
There's a bug in the OS/2 WARP REXX interpreter which allows the use of files with any extension or without an extension for external REXX procedures. These files also don't need the leading comment.
Note that if there's a directory with the name of the external REXX procedure prior to the REXX procedure in your PATH, REXX tries to "execute" the directory and fails with the error REX0003 - Program is unreadable.
This bug was fixed in the WARP 3 FixPak #8 (Source: APAR PJ19105) - and is again there after WARP 3 FixPak #17. It's still there in WARP 4. And again fixed in Fixpak #6 for WARP 4.

SYS0192 or SYS0008 using PIPEs

Problem
An OS/2 command with two or more PIPEs (|) leads to the OS/2 error message SYS0192 or SYS0008.
Hint
The number of PIPEs in one command is limited in OS/2. See Using PIPEs for a workaround.

SYS0005: Access is denied

Problem
Trying to create a directory or file leads to the OS/2 error message SYS0005. although there's no file or directory with this name and you have write access to the drive.
Hint
Maybe you're using the name of a device driver for the file or directory to create. Check the list of the installed device driver - or use the code provided in Check if a name describes a device or a file to check if the name you're using is the name of an existing device. (see also Reserved directory & file names)

SYS0317: The system cannot find message 32799 in message file OSO001.MSG

Problem
Copying a file to a FAT-formatted media fails with the error message SYS0317:The system cannot find message 32799 in message file OSO001.MSG
Hint
This error occurs if OS/2 has problems with the EAs on the target drive. To solve the problem, you can try to do a CHKDSK drive /F (where drive is the drive specifier, e.g. C: or D:) on the target drive but that may not solve the problem in all cases.

A method that works in all cases is to remove the EAs from the source file (using EAUTIL sourceFile nul /s)

EXPOSE does not work as expected

Problem
You find that a variable used in the EXPOSE statement of a procedure is not visible in your routine.
Hint
There's a bug in the processing of the EXPOSE statement in Classic REXX (not in Object-Oriented REXX!):
If you expose a stem variable directly in the EXPOSE statement and also in a variable list in parentheses the order of the parameter for the EXPOSE statement is important. Try the example below to see what I mean.
To avoid this bug, you should always specify variable lists before variable names for EXPOSE statements

E.g. use

test: PROCEDURE expose (myVarList1) (myVarList2) myVar3 myVar4

instead of

test: PROCEDURE expose  myVar3 myVar4 (myVarList1) (myVarList2)

(see also The keyword instruction EXPOSE)

/* sample code to show the bug in the processing of the EXPOSE      */
/* statement in Classic REXX                                        */

                    /* define some variable lists for the EXPOSE    */
                    /* statement                                    */
  exposeList = 'test1. test2. test3.'
  exposeList1 = 'test1. testVar test2. test3.'

                    /* define some variables for the EXPOSE         */
                    /* statement                                    */
  testVar = 'Testing...'

  test1.0 = 2
  test1.1 = 'T1_1'
  test1.2 = 'T1_2'

  test2.0 = 2
  test2.1 = 'T2_1'
  test2.2 = 'T2_2'

  test3.0 = 2
  test3.1 = 'T3_1'
  test3.2 = 'T3_2'

                    /* show the values of the variables in the main */
                    /* procedure                                    */
  say 'Now in Main:'
  call ShowVariables

                    /* now call the various subroutines with        */
                    /* different EXPOSE statements.                 */
                    /* Note that all global variables should be     */
                    /* available in all subroutines!                */
  call TestProc1
  call TestProc2
  call TestProc3
  call TestProc4

 return
TestProc1: PROCEDURE expose (exposeList) test2. test1. testVar
  say 'Now in TESTPROC1: PROCEDURE expose (exposeList) test2. test1. testVar'
    call ShowVariables
  return

TestProc2: PROCEDURE expose test1. (exposeList)  TestVar
  say 'Now in TESTPROC2: PROCEDURE expose test1. (exposeList) TestVar'
    call ShowVariables
  return

TestProc3: PROCEDURE expose test2. (exposeList1)
  say 'Now in TESTPROC3: PROCEDURE expose test2. (exposeList1) '
    call ShowVariables
  return

TestProc4: PROCEDURE expose test1. (exposeList1)
  say 'Now in TESTPROC4: PROCEDURE expose test1. (exposeList1) test2. TestVar'
    call ShowVariables
  return

/* sample routine to show the value of all global variables         */
   ShowVariables:
     if datatype( test1.0 ) = 'NUM' then
     do
       do i = 0 to test1.0
         call CharOut, 'test1.' || i || ' is "' || test1.i || '" '
       end
       say
     end
     else
       say 'test1.0 is "' || test1.0 || '" (= NOT DEFINED!!!)'

     if datatype( test2.0 ) = 'NUM' then
     do
       do i = 0 to test2.0
         call CharOut, 'test2.' || i || ' is "' || test2.i || '" '
       end
       say
     end
     else
       say 'test2.0 is "' || test2.0 || '" (= NOT DEFINED!!!)'

     if datatype( test3.0 ) = 'NUM' then
     do
       do i = 0 to test3.0
         call CharOut, 'test3.' || i || ' is "' || test3.i || '" '
       end
       say
     end
     else
       say 'test3.0 is "' || test3.0 || '" (= NOT DEFINED!!!)'

     say 'TestVar is "' || testVar || '"'
   return

Here is the result from the code above executed under Classic REXX:

 Now in Main:
 test1.0 is "2" test1.1 is "T1_1" test1.2 is "T1_2"
 test2.0 is "2" test2.1 is "T2_1" test2.2 is "T2_2"
 test3.0 is "2" test3.1 is "T3_1" test3.2 is "T3_2"
 TestVar is "Testing..."
 Now in TESTPROC1: PROCEDURE expose (exposeList) test2. test1. testVar
 test1.0 is "2" test1.1 is "T1_1" test1.2 is "T1_2"
 test2.0 is "2" test2.1 is "T2_1" test2.2 is "T2_2"
 test3.0 is "2" test3.1 is "T3_1" test3.2 is "T3_2"
 TestVar is "Testing..."
 Now in TESTPROC2: PROCEDURE expose test1. (exposeList) TestVar
 test1.0 is "2" test1.1 is "T1_1" test1.2 is "T1_2"
 test2.0 is "TEST2.0" (= NOT DEFINED!!!)
 test3.0 is "TEST3.0" (= NOT DEFINED!!!)
 TestVar is "Testing..."
 Now in TESTPROC3: PROCEDURE expose test2. (exposeList1)
 test1.0 is "2" test1.1 is "T1_1" test1.2 is "T1_2"
 test2.0 is "2" test2.1 is "T2_1" test2.2 is "T2_2"
 test3.0 is "TEST3.0" (= NOT DEFINED!!!)
 TestVar is "Testing..."
 Now in TESTPROC4: PROCEDURE expose test1. (exposeList1) test2. TestVar
 test1.0 is "2" test1.1 is "T1_1" test1.2 is "T1_2"
 test2.0 is "TEST2.0" (= NOT DEFINED!!!)
 test3.0 is "TEST3.0" (= NOT DEFINED!!!)
 TestVar is "TESTVAR"