Faster NSAppleScript alternative to AppleScript's "run script" command

AppleScript’s run script command is an extremely useful tool that allows the user to programatically run an AppleScript script in the form of a text string, text file, compiled script file, compiled script bundle, or application bundle. A potential downside of the run script command is the execution overhead time it incurs, typically between a couple of hundredths of a second in the best of cases to several tenths of a second in the worst. Although this time cost may not be important in many circumstances, it can become a prohibitive obstacle to the command’s use, for example, in a large repeat loop.

The following handler, which utilizes Cocoa’s NSAppleScript class to execute the AppleScript script, reproduces the behavior of the run script command but runs 10 to 15 times faster in my timing tests. Typical run times for very simple scripts have ranged from 1 to 2 thousandths of a second, which appears to be its execution overhead time. The handler accepts forms of script input similar to that of run script: a text string, or a reference to a text file, compiled script file, compiled script bundle, or application bundle. The reference to a script file or bundle may be in the form of a POSIX path, HFS path, or AppleScript alias. Error checking is performed at various steps, and an error is thrown to the calling program if one occurs. The result of script execution is returned to the calling program as a native AppleScript value by coercing the NSAppleEventDescriptor object returned by the NSAppleScript object execution.

Here are examples of handler usage:


-- Example of a text string script returning native AppleScript values

set scriptAsText to "return {1, 2.2, \"three\", true, current date, path to library folder from local domain, {1, 2, 3}, {aa:1, bb:2, cc:3}}"
return runScript(scriptAsText) --> the AppleScript record {1, 2.2, "three", true, date "Friday, November 1, 2019 at 3:35:36 PM", alias "Macintosh HD:Library:", {1, 2, 3}, {aa:1, bb:2, cc:3}}

-- Example of a script saved as a text file returning native AppleScript values

set scriptPath to ((path to desktop as text) & "SampleScript.applescript")'s POSIX path
do shell script "echo " & scriptAsText's quoted form & " >" & scriptPath's quoted form
return runScript(scriptPath) --> the AppleScript record {1, 2.2, "three", true, date "Friday, November 1, 2019 at 3:35:36 PM", alias "Macintosh HD:Library:", {1, 2, 3}, {aa:1, bb:2, cc:3}}

-- Example of a text string script returning a record of application object values

set scriptAsText to "tell application \"Finder\" to return (get properties of file 1 of desktop)"
return runScript(scriptAsText) --> the Finder record {name:..., index:..., displayed name:..., name extension:... }

Here is the handler:


use framework "Foundation"
use scripting additions

on runScript(theScript)
	-- The handler input argument is a reference to an AppleScript script in any of the following forms: text string, text file, compiled script file, compiled script bundle, or application bundle
	-- The handler return value is the AppleScript value returned by execution of the input script
	script util
		on processError(errorDict)
			set errorNumber to missing value
			try
				set errorNumber to (errorDict's NSAppleScriptErrorNumber) as integer
			end try
			if errorNumber ≠ missing value then
				set errorMessage to "missing value"
				try
					set errorMessage to (errorDict's NSAppleScriptErrorMessage) as text
				end try
				if errorMessage = "missing value" then set errorMessage to "[No error message]"
				error ("Problem with handler runScript:" & return & return & errorMessage) number errorNumber
			end if
		end processError
	end script
	-- Test if the input argument is a file reference
	set scriptAlias to missing value
	try
		set scriptAlias to theScript as alias
	on error
		try
			set scriptAlias to theScript as POSIX file as alias
		end try
	end try
	-- Create an NSAppleScript object from the input argument
	if scriptAlias ≠ missing value then
		-- If the input argument is a file reference, try to create an NSAppleScript object from the file, or throw an error if one occurs
		set scriptURL to current application's |NSURL|'s fileURLWithPath:(scriptAlias's POSIX path)
		set {scriptObj, errorDict} to current application's NSAppleScript's alloc()'s initWithContentsOfURL:(scriptURL) |error|:(reference)
		if errorDict ≠ missing value then util's processError(errorDict as record)
	else
		-- If the input argument is not a file reference, try to create an NSAppleScript object directly from its text content, or throw an error if one occurs
		set scriptObj to current application's NSAppleScript's alloc()'s initWithSource:(theScript)
		if scriptObj = missing value then util's processError({NSAppleScriptErrorMessage:"The input argument neither is a reference to an existing file nor can it be formed into an executable AppleScript object.", NSAppleScriptErrorNumber:-2700})
	end if
	-- If an NSAppleScript object was successfully created, try to execute the script, or throw an error if one occurs
	set {returnValue, errorDict} to (scriptObj's executeAndReturnError:(reference)) as list
	if errorDict ≠ missing value then util's processError(errorDict as record)
	-- If the script executed successfully, coerce the returned NSAppleEventDescriptor object into its equivalent AppleScript value, and return the AppleScript value to the calling program
	tell returnValue's descriptorType() to set {isList, isRecord, isNothing} to {it = 1.818850164E+9, it = 1.919247215E+9, it = 1.853189228E+9}
	if isList then
		return returnValue as list
	else if isRecord then
		return returnValue as record
	else if isNothing then
		return
	else
		return (returnValue as list)'s first item
	end if
end runScript

Note: To those who know more about NSAppleEventDescriptor-to-Applescript value coercion than I, may I ask if there is a better way to test for descriptor type than the numerical testing I’m using? I tried but failed to test against descriptor type enums, for instance typeAEList for a list, which for some reason weren’t recognized during compilation. More broadly speaking, is there a better way to perform the coercion?

You could do this:

(current application's NSAppleEventDescriptor's descriptorWithTypeCode:(returnValue's descriptorType()) as type class

which will give you the AppleScript type class explicitly, e.g. list, but that doesn’t avoid the if…then…else affair.

You can get AppleScript to choose the most appropriate type class by doing:

return the returnValue as {list, record, number, text, anything}

but, unless you know you’ll be dealing with specific types upon return and can avoid including anything in that list, it would sit a bit uneasy were it me (if you know your return type is either going to be a list or a record, say, then this works fine).

But I think Shane’s trick of getting AppleScriptObjC to unwrap the contents of an array/list is probably the safest way to go:

item 1 of ((current application's NSArray's arrayWithObject:returnValue) as list)

CK, thank you for the suggestions.

When I try the …as type class approach, Script Debugger removes the final keyword class during compilation, and the method fails. Any idea what’s going on with that?

I like Shane’s item 1 of array approach. It does have one loophole, namely when the executed script returns nothing (for example, when the script quits with a return command that has no argument.) My numerical approach tests for that with: it = 1.853189228E+9 If I could somehow test for that outlier case corresponding to <NSAppleEventDescriptor: null()>, I could then implement the item 1 of array approach. Is there a way to test for that special case?

Thanks again for your help.

For what it’s worth, I did find a way to test the NSAppleEventDescriptor return value for a value of “nothing” (other than testing its descriptorType for the numerical value 1.853189228E+9). The following appears to return true only when the descriptor = “nothing”:


tell returnValue to set isNothing to (its |data|()'s |length|() = 0) and (its stringValue() = missing value)

Still, this seems like a rather roundabout way of getting at this value.

Here’s a variation of your code. It requires 10.11, and it is a little simpler. It handles the nullDescriptor case, and it does the error stuff inline for simplicity.

use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use scripting additions

on runScript(theScript)
	set theScript to (current application's NSArray's arrayWithObject:theScript)'s firstObject()
	if (theScript's isKindOfClass:(current application's |NSURL|)) as boolean then
		set {scriptObj, errorDict} to current application's NSAppleScript's alloc()'s initWithContentsOfURL:(scriptURL) |error|:(reference)
		if errorDict is not missing value then
			set {theError, errorNumber} to theDict's objectsForKeys:{current application's NSAppleScriptErrorMessage, current application's NSAppleScriptErrorNumber} notFoundMarker:"[No error message]"
			error theError as text number errorNumber as integer
		end if
	else
		set scriptObj to current application's NSAppleScript's alloc()'s initWithSource:(theScript)
		if scriptObj = missing value then error "The input argument neither is a reference to an existing file nor can it be formed into an executable AppleScript object." number -2700
	end if
	set {returnValue, errorDict} to scriptObj's executeAndReturnError:(reference)
	if errorDict is not missing value then
		set {theError, errorNumber} to theDict's objectsForKeys:{current application's NSAppleScriptErrorMessage, current application's NSAppleScriptErrorNumber} notFoundMarker:"[No error message]"
		error theError as text number errorNumber as integer
	end if
	if (returnValue's isEqual:(current application's NSAppleEventDescriptor's nullDescriptor())) as boolean then return
	return item 1 of ((current application's NSArray's arrayWithObject:returnValue) as list)
end runScript

Speed-wise, it’s a whisker faster using your sample script, but there’s very little in it.

However…

My tests in Script Geek show that the speed of using run script is actually faster than both – the ASObjC versions take about 25% longer with your sample code.

I wonder how you did your tests. If you did them in a script editor, that would explain the difference – code involving Apple events always gets an inflated time because of the extra overhead of log instrumentation.

Shane,

Thank you pointing out the null-descriptor test for no return value. That, combined with the [array → list → item 1] coercion method, is a nice compact way of converting an NSAppleEventDescriptor result to an AppleScript value. I find that it runs about 10% slower (give or take) than my original method of testing against numerical constants. Given that the numerical test executes a bit faster, is there a downside to using it? For example, how great is the risk that Apple might change the numerical constants at some arbitrary point in the future?

Regarding execution speed, here is a sample execution speed test:


set scriptAsText to "return {1, 2.2, \"three\", true, current date, path to library folder from local domain, {1, 2, 3}, {aa:1, bb:2, cc:3}}"

-- Each handler is executed once prior to beginning the timing tests in the hope that this might mitigate spurious effects on execution times due to caching.

set x1 to my runScript(scriptAsText)
set x2 to my runScriptNEW(scriptAsText)
set x3 to my |AppleScript's run script|(scriptAsText)

set nn to 100

set t1 to current application's CACurrentMediaTime()
repeat nn times
	set x1 to my runScript(scriptAsText)
end repeat
set t1 to ((current application's CACurrentMediaTime()) - t1) / nn

set t2 to current application's CACurrentMediaTime()
repeat nn times
	set x2 to my runScriptNEW(scriptAsText)
end repeat
set t2 to ((current application's CACurrentMediaTime()) - t2) / nn

set t3 to current application's CACurrentMediaTime()
repeat nn times
	set x3 to my |AppleScript's run script|(scriptAsText)
end repeat
set t3 to ((current application's CACurrentMediaTime()) - t3) / nn

tell application "System Events"
	activate
	display dialog "t1=" & t1 & return & "t2=" & t2 & return & "t3=" & t3 & return & "t2 / t1=" & t2 / t1 & return & "t3 / t1=" & t3 / t1 & return & "t3 / t2=" & t3 / t2
end tell

on runScript(theScript)

	#   My originally submitted script, including the following (original) ending...   #

	-- If the script executed successfully, coerce the returned NSAppleEventDescriptor object into its equivalent AppleScript value, and return the AppleScript value to the calling program
	tell returnValue's descriptorType() to set {isList, isRecord, isNothing} to {it = 1.818850164E+9, it = 1.919247215E+9, it = 1.853189228E+9}
	if isList then
		return returnValue as list
	else if isRecord then
		return returnValue as record
	else if isNothing then
		return
	else
		return (returnValue as list)'s first item
	end if
end runScript

on runScriptNEW(theScript)
	
	#   My originally submitted script, except with the [null-descriptor] test and [array -> list -> item 1] coercion...   #

	-- If the script executed successfully, coerce the returned NSAppleEventDescriptor object into its equivalent AppleScript value, and return the AppleScript value to the calling program
	if (returnValue's isEqual:(current application's NSAppleEventDescriptor's nullDescriptor())) as boolean then return
	return ((current application's NSArray's arrayWithObject:returnValue) as list)'s first item
end runScriptNEW

on |AppleScript's run script|(theScript)

	#   Using AppleScript's [run script] command   #

	return run script theScript
end executeRunScript

Here are typical results, reproduced on multiple timing tests. I don’t find any discernible effect on execution times of changing the order in which the handlers are executed.

When the timing script is run as an applet via the Finder:

t1=0.0018
t2=0.0021
t3=0.0197 (9 to 10x slower than t1 or t2)

When the timing script is run as a script via LaunchBar:

t1=0.0020
t2=0.0020
t3=0.0227 (11x slower than t1 or t2)

When the timing script is run as a script via Script Debugger:

t1=0.0017
t2=0.0019
t3=0.0211 (11 to 12x slower than t1 or t2)

In these tests, AppleScript’s run script command reproducibly executes 9 to 12x slower than my script, and my script as originally submitted is a bit faster than that incorporating the [null-descriptor] test and [array → list → item 1] coercion.

In Script Editor, I’m getting similar results to Shane’s with regard to relative speeds, although they can vary with parameter scripts which take a long time to run. They’re both interesting handlers, though. Thanks, fellas! :slight_smile:

This is interesting too. I’d never seen it before. But with the classes in the order above, while NSArrays are returned as lists, other cocoa types are returned as AS values in lists. Changing the order of classes has different side effects, depending on the value being coerced. A simple ‘as anything’ works with all the cocoa and AS values I’ve tried so far, but it’s not an official AS coercion!

My guess would be somewhat less than 1 in 1.853189228E+9. That’s simply the integer value of the four-letter code ‘null’:

current application's NSHFSTypeCodeFromFileType("'null'")

That’s very interesting. I’m surprised at the results in an applet; they’re less efficient when events are involved than I thought.

set x to current application's NSArray's arrayWithObject:{1}
x as anything --> 1

It’s also a bit tricky. It’s essentially the wildcard, «class ****», and in pre-OS X it used to be defined as any. Script Debugger defines any and anything as synonyms for backwards compatibility, and the compiler can choose a different version under differing (undefined) conditions.

Before third-party scripting additions died, there was also a wrinkle with Satimage.osax, which defined four terms with a value of ‘****’: list of alias or list of string, list of string or string, list of real or real, and list of matchrecord, list of string, machrecord or string. You would get one of these if the script also had a use scripting additions statement, which meant the addition terminology was loaded first. Which one you got depended a bit on the version of Satimage.osax.

Thanks for the great information. They say that death and taxes are the only two certainties in life. Maybe we could add 1.853189228E+9 for the null value to that list!

I too find that AppleScript’s run script command executes a bit faster than my runScript handler when I run my timing script via Script Editor. On the other hand, my runScript handler runs about 10 to 15x faster than AppleScript’s run script command in every other script runner I tested, including:

- Script Debugger (as a script)
- Finder (as an applet)
- LaunchBar (as a script)
- Terminal (as an osascript)
- Automator (via the [i]Run as AppleScript[/i] service)
- Scripts menu on the right side of the menu bar (as a script)
- Objective-C application (via the Finder, running the timing script as an NSAppleScript from the script text)
- Objective-C application (via the Finder, running the timing script as an NSAppleScript from the compiled script)
- Script Libraries script (as a handler in the utility script)

Not sure how to explain this discrepancy, but it would appear that Script Editor is somehow avoiding the ~0.02 second overhead cost of AppleScript’s run script command present with all the other script runners.

P.S. To add another monkey wrench to the works, when I run the latter two Objective-C applications from within Xcode, AppleScript’s run script command continues to incur the ~0.02 second overhead cost, but my runScript handler now incurs a ~0.01 second overhead cost not seen with any of the other script runners, including Script Editor. My runScript handler is still faster than AppleScript’s run script command in this specific case, but now by about 2.5x rather than 10 to 15x.

Edit note: The Objective-C tests were added after I had originally submitted this post.

Edit note 2: The Script Libraries script result was also added after I had originally submitted this post.

Here is a slightly improved version of my originally submitted handler. It allows the input script to be in the form of a text string, or a POSIX path, HFS path, AppleScript alias, POSIX file («class furl»), or NSURL object (like Shane’s script) pointing to a script text file, compiled script file, compiled script bundle, or application bundle. It also has minor improvements in error handling. The numerical comparison method of coercing the return value was retained simply because it executes the fastest.


use framework "Foundation"
use scripting additions

on runScript(theScript)
	-- The handler input argument is a reference to an AppleScript script in any of the following forms: text string, or a POSIX path, HFS path, AppleScript alias, POSIX file («class furl»), or NSURL object pointing to a script text file, compiled script file, compiled script bundle, or application bundle
	-- The handler return value is the AppleScript value returned by execution of the input script
	script util
		on processError(errorRecord)
			set errorNumber to missing value
			try
				set errorNumber to (errorRecord's NSAppleScriptErrorNumber) as integer
			end try
			if errorNumber = -128 then
				error number -128
			else if errorNumber ≠ missing value then
				set errorMessage to "missing value"
				try
					set errorMessage to (errorRecord's NSAppleScriptErrorMessage) as text
				end try
				if errorMessage = "missing value" then set errorMessage to "[No error message]"
				if errorNumber ≠ -2700 then set errorMessage to "(" & errorNumber & ")  " & errorMessage
				error ("Problem with handler runScript:" & return & return & errorMessage) number errorNumber
			end if
		end processError
	end script
	-- Test if the input argument is a file reference, and if so, form it into an NSURL object
	tell theScript
		set {scriptURL, isNonURLFileRef} to {missing value, true}
		try
			set scriptAlias to it as alias
		on error
			try
				set scriptAlias to it as POSIX file as alias
			on error
				set isNonURLFileRef to false
				try
					if (its isKindOfClass:(current application's |NSURL|'s class)) then set scriptURL to it
				end try
			end try
		end try
		if isNonURLFileRef then set scriptURL to current application's |NSURL|'s fileURLWithPath:(scriptAlias's POSIX path)
	end tell
	-- Create an NSAppleScript object from the input argument
	if scriptURL ≠ missing value then
		-- If the input argument is a file reference, try to create an NSAppleScript object from the file, or throw an error if one occurs
		set {scriptObj, errorDict} to current application's NSAppleScript's alloc()'s initWithContentsOfURL:(scriptURL) |error|:(reference)
		if errorDict ≠ missing value then util's processError(get errorDict as record)
	else
		-- If the input argument is not a file reference, try to create an NSAppleScript object directly from its text content, or throw an error if one occurs
		set scriptObj to current application's NSAppleScript's alloc()'s initWithSource:(theScript)
		if scriptObj = missing value then util's processError({NSAppleScriptErrorMessage:"The input argument neither is a reference to an existing file nor can it be formed into an executable AppleScript object.", NSAppleScriptErrorNumber:-2700})
	end if
	-- If an NSAppleScript object was successfully created, try to execute the script, or throw an error if one occurs
	set {returnValue, errorDict} to (scriptObj's executeAndReturnError:(reference)) as list
	if errorDict ≠ missing value then util's processError(get errorDict as record)
	-- If the script executed successfully, coerce the returned NSAppleEventDescriptor object into its equivalent AppleScript value, and return the AppleScript value to the calling program
	tell returnValue's descriptorType() to set {isList, isRecord, isNothing} to {it = 1.818850164E+9, it = 1.919247215E+9, it = 1.853189228E+9}
	if isList then
		return returnValue as list
	else if isRecord then
		return returnValue as record
	else if isNothing then
		return
	else
		return (returnValue as list)'s first item
	end if
end runScript

Quite right. I wrote that reply in a bit of a multi-tasking rush, and I think it popped into my head later when I was trying to sleep that the order of that class list, as I had printed it, wasn’t the best order. Namely, record should come before list always. Coercions likely to throw errors are best featured early in the list; and valid coercions are usually better positioned after the true class.

Sometimes it’s easier/necessary to split one list into two serial coercion lists, i.e. as {doh, ray, mee} as {fah, so}, depending what one’s overall objective is.

But, in truth, I haven’t discerned a system of rules to for ordering that universally applies to every subset (sublist) of type classes, so the final order requires experimenting with but is, in my opinion, typically worth it for code that’s much cleaner/easier to read. I wonder if there are any performance differentials compared to if…then…else. When I get a working Mac, I shall have to test.

I would like to implement your novel coercion method if a universal scheme could be developed that handled values of all AppleScript and non-AppleScript classes, which this general purpose script requires. I suspect that it would execute at least as fast as the if…then…else… technique I am currently using.

Shane’s helpful elaboration of the meaning of the descriptor numerical values lends confidence to using the raw numbers for type testing for the sake of execution speed:


For an AppleScript list: current application's NSHFSTypeCodeFromFileType("'list'") = 1.818850164E+9
For an AppleScript record: current application's NSHFSTypeCodeFromFileType("'reco'") = 1.919247215E+9
For no AppleScript value: current application's NSHFSTypeCodeFromFileType("'null'") = 1.853189228E+9

(I examined the relative speed of testing against the raw numbers versus against the above NSHFSTypeCodeFromFileType return values. The cost of the latter was small, about 0.00003 seconds per conversion or about 0.0001 seconds overall in view of the three conversions, but since the raw-number method is a tiny bit faster, I figured I might as well stay with it.)

Apologies for the multiple posts of the script code, but I felt compelled to submit this further improved version that handles errors more effectively. It wraps the entire handler code in a try block so that all errors are captured. It implements the objectsForKeys:notFoundMarker: method for retrieving error dictionary values, as Shane demonstrated in his script. And it posts error messages that are more topic-specific. The comments were also tuned up a bit.


on runScript(theScript)
	-- The handler input argument is a reference to an AppleScript script in the form of a text string, or a POSIX path, HFS path, AppleScript alias, POSIX file («class furl»), or NSURL object pointing to a script text file (e.g., .applescript), compiled script file (.scpt), compiled script bundle (.scptd), or application bundle (.app)
	-- The handler returns the value returned by execution of the input script; the value may be an AppleScript value or an ASObjC or other application object
	script util
		property errorSubheader : missing value
	end script
	-- Wrap the code in a try block to capture any error that may occur
	try
		-- If the input argument is a file reference, form it into an NSURL object
		tell theScript
			set {scriptURL, isNonURLFileRef} to {missing value, true}
			try
				set scriptAlias to it as alias
			on error
				try
					set scriptAlias to it as POSIX file as alias
				on error
					set isNonURLFileRef to false
					try
						if (its isKindOfClass:((current application's |NSURL|)'s |class|())) then set scriptURL to it
					end try
				end try
			end try
			if isNonURLFileRef then set scriptURL to (current application's |NSURL|)'s fileURLWithPath:(scriptAlias's POSIX path)
		end tell
		-- Create an NSAppleScript object from the input argument
		if scriptURL ≠ missing value then
			-- If the input argument is a file reference, try to create an NSAppleScript object from the file, or throw an error if one occurs
			set {scriptObj, errorDict} to (current application's NSAppleScript)'s alloc()'s initWithContentsOfURL:(scriptURL) |error|:(reference)
			if errorDict ≠ missing value then
				set util's errorSubheader to "Cannot form an executable NSAppleScript object from the input file reference due to the following error:"
				set {errorMessage, errorNumber} to (errorDict's objectsForKeys:{current application's NSAppleScriptErrorMessage, current application's NSAppleScriptErrorNumber} notFoundMarker:"missing value") as list
				if errorNumber = "missing value" then set {errorMessage, errorNumber} to {get errorDict's |description|() as text, -2700}
				error errorMessage number errorNumber
			end if
		else
			-- If the input argument is not a file reference, try to create an NSAppleScript object directly from its text content, or throw an error if one occurs
			set scriptObj to (current application's NSAppleScript)'s alloc()'s initWithSource:(theScript)
			if scriptObj = missing value then error "The input argument is neither a text string script nor a reference to an existing script file."
		end if
		-- If an NSAppleScript object was successfully created, try to execute the script, or throw an error if one occurs
		set util's errorSubheader to "AppleScript execution error:"
		set {returnValue, errorDict} to (scriptObj's executeAndReturnError:(reference)) as list
		if errorDict ≠ missing value then
			set {errorMessage, errorNumber} to (errorDict's objectsForKeys:{current application's NSAppleScriptErrorMessage, current application's NSAppleScriptErrorNumber} notFoundMarker:"missing value") as list
			if errorNumber = "missing value" then set {errorMessage, errorNumber} to {get errorDict's |description|() as text, -2700}
			error errorMessage number errorNumber
		end if
		-- If the script executed successfully, coerce the returned NSAppleEventDescriptor object into the original AppleScript value or ASObjC or other application object returned by the script execution, and return the coerced value to the calling program
		set util's errorSubheader to missing value
		tell returnValue's descriptorType() to set {isList, isRecord, isNothing} to {it = 1.818850164E+9, it = 1.919247215E+9, it = 1.853189228E+9}
		if isList then
			return returnValue as list
		else if isRecord then
			return returnValue as record
		else if isNothing then
			return
		else
			return (returnValue as list)'s first item
		end if
	on error errorMessage number errorNumber
		if errorNumber = -128 then error number -128
		if errorMessage = "missing value" then set errorMessage to "[No error message]"
		if errorNumber ≠ -2700 then set errorMessage to "(" & errorNumber & ") " & errorMessage
		if util's errorSubheader ≠ missing value then set errorMessage to util's errorSubheader & return & return & errorMessage
		error ("Problem with handler runScript:" & return & return & errorMessage) number errorNumber
	end try
end runScript