inconsistent "malloc, Freed Object" error in iTunes-Calendar applet

I have been re-writing an iTunes-via-Calendar-Event controller script (evolved from a previous project https://macscripter.net/viewtopic.php?id=45383, current script below.)

The script is triggered by a calendar event; it is saved as an application, and code signed with my developer ID.

Basically, the Applet parses the Event Summary and queues a playlist with the name of the event.

The Applet is not currently reliable because of an unpredictably occurring error, always the exact same error. To my experience so far, this error only occurs when the Applet is called via the calendar alert; the Applet always works when run from the Finder.

I am not a programmer and cannot find any information to explain this behavior. “memory allocation" seems to be an actual programming thing, not an AppleScript problem.

I think the most likely explanation is a conflict in “CalendarLib.” I would appreciate any insight; a simple search does not reveal any other people with this problem https://duckduckgo.com/?q=applescript+CalendarLib+malloc, so perhaps my implementation is flawed (maybe “fetch events starting date RightNow ending date RightNow” is bad syntax…?)

(the script uses “CalendarLib EC,” functionally identical but without the requirement to use “Script Debugger.”)

Is there an alternate, reliable method of deriving Calendar’s “EVENT SUMMARY” from the current, occurring event…? I will use that instead. The script does not add, change, move, or modify events in any way, I do not need any of those controls—i just want the script to get the name of the current event, to determine the playlist.

Here is the current script:

use script "CalendarLib EC" version "1.1.3"
-- https://macosxautomation.com/applescript/apps/Script_Libs.html#CalendarLib
use scripting additions

global theTimeRightNow
set theTimeRightNow to current date
set theEvents to {}
set theStore to fetch store
set theCal to fetch calendar "TV" cal type cal local event store theStore
set theEvents to fetch events starting date theTimeRightNow ending date theTimeRightNow searching cals {theCal} event store theStore
if theEvents is not {} then
	set theEvent to (event info for event (first item of theEvents))
	set theEventName to {event_summary of theEvent} as string
else
	set theEventName to "> Dead Air" -- default to music playlist if calendar event unclear
end if

set theEventType to the first character of theEventName
set theEventName to parseTheText(theEventName, theEventType & " ") as string -- strip theEventType from name

if theEventType is "#" then SignOff() -- expects "# {optional identifier}"
if theEventType is "*" then MakeMix(theEventName) -- expects "* Playlist1,Playlist2,MixedName"
if theEventType is ">" then PlayShow(theEventName) -- expects "> Playlist"

--tell me to quit -- not sure a "quit" call is necessary...

to PlayShow(theEventName)
	set playlistMergedName to "Up Next" -- leave current "Now Playing" going while we assemble the next playlist
	set thePlaylistNameWhenItStartsPlaying to "Now Playing"
	set PlaylistFiller to "[LOOPS]"
	set PlaylistInterstitialsName to "[STATION ID-D]" -- intersitials for the [D]aytime broadcast
	if ((hours of (theTimeRightNow)) > 21) or ((hours of (theTimeRightNow)) < 5) then set PlaylistInterstitialsName to "[STATION ID-N]" -- unless it’s [N]ight
	MixPlaylists(theEventName, PlaylistInterstitialsName, playlistMergedName)
	tell application "iTunes"
		set theTrack to some track of playlist PlaylistFiller -- finish by adding a single "LOOP" track to fill out the hour
		set enabled of (duplicate theTrack to playlist playlistMergedName) to true
		-- maybe DIM SCREEN or ACTIVATE SCREENSAVER or something to conceal playlist switch
		activate
		set full screen to true
		set shuffle enabled to false
		set song repeat to off -- one, all, off
		set sound volume to 90
		play playlist playlistMergedName -- switch to "Up Next"
		if (exists playlist thePlaylistNameWhenItStartsPlaying) then delete playlist thePlaylistNameWhenItStartsPlaying -- get rid of old "Now Playing"
		tell playlist playlistMergedName to set name to thePlaylistNameWhenItStartsPlaying -- change "Up Next" to "Now Playing"
		pause -- no idea why but sometimes iTunes starts playing "offscreen"...
		-- maybe RESTORE SCREEN here
		play -- ...this pause/play is purely to get itunes playing fullscreen & onscreen
	end tell
end PlayShow

to MakeMix(theEventName) -- preshuffle two playlists, sometimes called at 3:am to prep for later in day
	set ThePlaylistsNames to parseTheText(theEventName, ",")
	set {sourcePlaylistName1, sourcePlaylistName2, MixedPlaylistName} to {item 1 of ThePlaylistsNames, item 2 of ThePlaylistsNames, item 3 of ThePlaylistsNames}
	MixPlaylists(sourcePlaylistName1, sourcePlaylistName2, MixedPlaylistName)
end MakeMix

to SignOff()
	tell application "iTunes"
		activate
		stop
		set full screen to false -- is this even necessary?
		close window 1
	end tell
end SignOff

to MixPlaylists(sourcePlaylistName1, sourcePlaylistName2, MixedPlaylistName)
	tell application "iTunes"
		if (not (exists playlist MixedPlaylistName)) then
			set MixedPlaylist to make new user playlist with properties {name:MixedPlaylistName} -- if playlist does not exist, make it
		else
			set MixedPlaylist to user playlist MixedPlaylistName -- if playlist exists, select it & clean it out
			delete tracks of MixedPlaylist
		end if
		set theTrackCount to (count of tracks of playlist sourcePlaylistName1) -- length will be based on sourcePlaylistName1
		--Shuffle based on “CK's Evenly-shuffled Playlists v0.6” by Charles Kelly http://www.manythings.org/mac/
		repeat with i from 1 to theTrackCount
			if sourcePlaylistName2 contains "STATION" then
				set theTrack to some track of playlist sourcePlaylistName2 -- Interstitials
			else
				set theTrack to track i of playlist sourcePlaylistName2 -- sequential
			end if
			set enabled of (duplicate theTrack to MixedPlaylist) to true
			set theTrack to track i of playlist sourcePlaylistName1
			set enabled of (duplicate theTrack to playlist MixedPlaylistName) to true
			set unplayed of theTrack to false
		end repeat
	end tell
end MixPlaylists

to parseTheText(theText, theDelimiter)
	set {originalDelimiters, my text item delimiters} to {my text item delimiters, theDelimiter}
	set parsedText to text items of theText
	set my text item delimiters to originalDelimiters
	return parsedText
end parseTheText

Here is an example of a calendar day: (yes, Saturday is all cartoons… i mean, come on.)

There’s not much to go on there. Two suggestions:

  • Try adding a short delay after the script is triggered and before it accesses the calendar database. Unlikely to do anything, but there may be an issue with the database changing as part of the triggering.

  • Add logging statements between the relevant lines to see if the error happens in a consistent place.

Thank you for the reply! these are both excellent suggestions. Previous versions of this script used copious logging for debugging, and lots of delays to keep the script from getting ahead of the computer; i edited out both systems over time… i will re-institute them now.

—AE

Since introducing logging, i’ve had five successful runs and two “abort called, malloc” failures. The failures came consistently after this command:

set theEvents to {}

(upon review, i’m not sure why that line was there… i must’ve been trying to “solve” something…)

I have commented that line out, and will see if the Applet makes it through the day.

—AE

another couple successful runs, then an abort.

“set theEvents to {}” is commented out. Logging reports “Applet started…” but does not get to “fetched store.”

something at fetch store is getting the error? I’m going to review my syntax.

--set theEvents to {}
LogThisAction("Applet Started...")
set theStore to fetch store
LogThisAction("fetched store")
set theCal to fetch calendar "TV" cal type cal local event store theStore

–AE

in the interest of full reporting…

here is the current (top part of) the script…

use script "CalendarLib EC" version "1.1.3" -- https://macosxautomation.com/applescript/apps/Script_Libs.html#CalendarLib
use scripting additions

LogThisAction("Applet Started...")

global theTimeRightNow
LogThisAction("established time variable...")

set theTimeRightNow to current date
LogThisAction("set time...")

delay 1
LogThisAction("delayed for 1 second...")

--set theEvents to {}
set theStore to fetch store
LogThisAction("fetched store")
set theCal to fetch calendar "TV" cal type cal local event store theStore
LogThisAction("set calendar")
set theEvents to fetch events starting date theTimeRightNow ending date theTimeRightNow searching cals {theCal} event store theStore
LogThisAction("fetched events")
if theEvents is not {} then
	set theEvent to (event info for event (first item of theEvents))
	set theEventName to {event_summary of theEvent} as string
	LogThisAction("derived event name " & theEventName)
	
else
	LogThisAction("event name unclear, going with Dead Air")
	set theEventName to "> Dead Air" -- default to music playlist if calendar event unclear
end if

here is the logging subroutine

to LogThisAction(themessage)
	set theLogFile to "TV-" & (do shell script "date  +'%Y%b%d'" as string) & ".log" -- make file named "TV-YearMonthDay.log"
	set theLine to (do shell script "date  +'%a %d-%b-%Y %H:%M'" as string) & " '" & themessage & "'" -- quoted $message to avoid trouble characters
	do shell script "echo " & theLine & " >> ~/Documents/" & theLogFile
end LogThisAction

and here is an example of the resulting log…

that last incomplete cycle results in

full text available here https://pastebin.com/FJ9r3pEA

–AE

Hi.

CalendarLib EC’s fetch store, fetch calendar, and possibly fetch events commands return memory pointers rather than AppleScript values. Since you’re storing these in persistent (ie. run handler) variables, your applet, when run as such, will attempt to save these back into itself, which I believe isn’t allowed. Does it help if you make all the variables local? You can put your top-level code into an ordinary handler to achieve this. theTimeRightNow doesn’t need to be a global either. Its value can be passed to PlayShow() as an additional parameter.

use script "CalendarLib EC" version "1.1.3"
-- https://macosxautomation.com/applescript/apps/Script_Libs.html#CalendarLib
use scripting additions

main() -- Only this call in the implicit run handler. No variables.

on main() -- All variables in this and the other handlers are local by default.
	set theTimeRightNow to current date
	
	set theEvents to {}
	set theStore to fetch store
	set theCal to fetch calendar "TV" cal type cal local event store theStore
	set theEvents to fetch events starting date theTimeRightNow ending date theTimeRightNow searching cals {theCal} event store theStore
	if theEvents is not {} then
		set theEvent to (event info for event (first item of theEvents))
		set theEventName to {event_summary of theEvent} as string
	else
		set theEventName to "> Dead Air" -- default to music playlist if calendar event unclear
	end if
	
	set theEventType to the first character of theEventName
	set theEventName to parseTheText(theEventName, theEventType & " ") as string -- strip theEventType from name
	
	if theEventType is "#" then SignOff() -- expects "# {optional identifier}"
	if theEventType is "*" then MakeMix(theEventName) -- expects "* Playlist1,Playlist2,MixedName"
	if theEventType is ">" then PlayShow(theEventName, theTimeRightNow) -- expects "> Playlist"
	
	--tell me to quit -- not sure a "quit" call is necessary...
end main

to PlayShow(theEventName, theTimeRightNow)

	-- Rest of script as is.

this is fantastic. this is exactly what i wondered but could not put into words, because the concepts themselves were just beyond my modest ability to (cut-n-paste bits of other people’s work)!

Thank you so much. I will try this out right now.

–AE