Javascript Help? Handler is not defined

I was on Twine Q&A trying to get rollover text to work and found this

Most of it works except I get the error saying


Error: cannot execute macro <>: handler is not defined


And I can’t seem to find out why. I’m not very competent with java but html and css are slowly becoming second nature.

Hopefully someone can help me, thank you in advance!!

Twine Version: online version
Story Format: Sugarcube 2

Unfortunately, this forum tends to “eat” macros and HTML elements if you don’t select and then mark them using the “Preformatted text” button (the “</>” button) on the editor window. Since we can’t see what macro is throwing the error, it’s a bit more difficult to figure out where the problem is.

That said, it might be due to the “isAsync : true,” code in that old sample code, since I don’t see that in the Macro API documentation. It’s possible (if not likely) that things have changed in SugarCube in the intervening 2 1/2 years since that code was written.

If your code is any different from the code on the page you linked to, it would be best to just post that code here within a “preformatted text” block, so we can see if there are any errors in it.

Thanks. :grinning:

P.S. The code you’re talking about is not Java code, it’s JavaScript. While they’re somewhat related, the old joke is that they’re like the words “car” and “carpet”, in that they may use the same word, but they’re quite different things.

Thank you for that. My code is slightly different cause of line continuation, but this is the javascript I have:

	<<hoverappend>>, <<hoverprepend>>, & <<hoverreplace>>
Macro.add(['hoverappend', 'hoverprepend', 'hoverreplace'], {
	isAsync : true,
	tags    : null,
	handler : function () {
		if (this.args.length === 0) {
			return this.error('no hover text specified');

		// Custom debug view setup.
		if (Config.debug) {
				this.source + this.payload[0].contents + '<</' + + '>>'

		const $hover     = jQuery(document.createElement('span'));
		const $insert    = jQuery(document.createElement('span'));
		const transition = this.args.length > 1 && /^(?:transition|t8n)$/.test(this.args[1]);

			.wikiWithOptions({ profile : 'core' }, this.args[0])
			.addClass('link-hover macro-' +
			.one('mouseenter', (function (macroName, contents) {
				return this.createShadowWrapper(function (event) {
					if (macroName === 'hoverreplace') {
					else {

					if (contents !== '') {
						const frag = document.createDocumentFragment();
						new Wikifier(frag, contents);

					if (transition) {
						setTimeout(function () {
							$insert.removeClass('macro-' + macroName + '-in');
						}, Engine.minDomActionDelay);
			})(, handler.payload[0].contents))

		$insert.addClass('macro-' + + '-insert');

		if (transition) {
			$insert.addClass('macro-' + + '-in');

		if ( === 'hoverprepend') {
		else {

Its not massively different but there are a few tweaks to make it do what I wanted.

Also I tried removing the isAsync : true and turning it to false and neither works

I didn’t do a thorough check of the code, but the problem you’re seeing is caused by this line:

})(, handler.payload[0].contents))

which should look like this instead:

})(, this.payload[0].contents))

Hopefully that works for you. :grinning: