[BUG] GM_addValueChangeListener ignores the first change from 3rd+ instance of script
See original GitHub issue- I have searched for existing issues that already reported this problem and found none
Describe the bug
When running a script in multiple tabs, GM_addValueChangeListener in the first tab will ignore the first change made by the 3rd+ instance of the same script.
To Reproduce I’ve made an example script using the concept of having a “host”, which is the first instance of the script, with subsequent instances of the script attempting to change a value to check if this “host” is already present. You can read the code comments to fully understand what is going on. In short, though, what is going on is the following:
- “present” is set to false
- if it is not set to true within 500ms, the script will become a “host”
- new script is loaded, and also sets present to true
- the original “host” sets present back to false. This causes the script to know that it is already loaded because another “host” is already present.
// ==UserScript==
// @name Test GM_addValueChangeListener
// @namespace Violentmonkey Scripts
// @match *://example.com/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addValueChangeListener
// @version 1.0
// @author Levi_OP
// @description 4/8/2022, 2:03:57 PM
// ==/UserScript==
// This code (in the if statement) only runs on the first time the script is executed. If the "present" value is undefined, it will be set to true and the script will assume the role of "host".
if (GM_getValue("present") === undefined) {
console.log('first time setup and becoming "host"')
GM_setValue("present", true);
GM_addValueChangeListener("present", (_name, oldValue, newValue, _remote) => {
console.log("present changed from %o to %o", oldValue, newValue)
if (!newValue) GM_setValue("present", true)
})
} else { // This code will run every time after the first run ("present" is defined)
GM_setValue("present", false); // First, "present" is set to false.
// Then, a timeout is set for half a second. If not cleared by the code below the timeout, it will assume the role of "host".
const timeout = setTimeout(() => {
console.log('response timeout. becoming "host"')
GM_setValue("present", true) // Setting present back to true (default, as the host is "preset")
// Event listener is added which ensures that if "present" is changed, it will be changed back immediately.
GM_addValueChangeListener("present", (_name, oldValue, newValue, _remote) => {
console.log("present changed from %o to %o", oldValue, newValue) // This also logs every time "present" is being changed. This shows that the 3rd+ instance is skipped.
if (newValue === false) GM_setValue("present", true)
})
}, 500)
// This event listener is added at the same time as the timeout. If "present" is changed back to true after we set it to false above, then we know that the host is present. We then clear the timeout so that our script does not try to become the new "host".
GM_addValueChangeListener("present", (_name, _oldValue, newValue, remote) => {
if (!remote) return;
if (newValue) {
clearTimeout(timeout)
console.log("response")
}
})
}
Steps to reproduce the behavior:
- Run the script above in three or more tabs
- Observe the console. (read “Actual behavior” for interpretation)
Expected behavior Every instance after the first of the script will log “response”, and all changes will be logged in the host’s console.
Actual behavior
- First instance of script will log “becoming ‘host’”
- Second instance of script will log “response”
- Third (and subsequent instances) will log “becoming ‘host’”.
Taking a look at the original host’s console, we can see that the event listener only sees when the script is set back to true by these new instances. For example:
response timeout. becoming "host"
present changed from true to false
present changed from false to true
present changed from false to true
As you probably know, GM_addValueChangeListener should only fire when the new value is different than the old value. The logs above would make it appear as though this isn’t the case, which doesn’t make sense.
I was writing code that used a concept similar to the example above, and rewrote it multiple times thinking there was some issue with my code. What convinced me otherwise was that running the same script as above in tampermonkey runs exactly how I would expect it to. The GM_addValueChangeListener catches every change from every instance of the script.
What is the result in the upcoming release? I thought that this might have something to do with #1214, but using the latest beta of violentmonkey changed nothing (probably a different issue).
Environment:
- OS: macOS Version 11.6 (Build 20G165)
- Browser: Chrome
- Browser Version: 100.0.4896.75 (Official Build) (x86_64)
- Violentmonkey Version: 2.13.0
Issue Analytics
- State:
- Created a year ago
- Comments:5 (4 by maintainers)

Top Related StackOverflow Question
Test build: zip.
Fixed in a5ab9bb7.