Dev Investigation Notes
When a script works in Chrome but fails only in Whale: Cannot destructure property of undefined
This is a translated article. Read the original in Korean ↗
On Android Whale only, the buttons wouldn't turn on after a video was selected. The problem went away after fingerprint.js was renamed to mediacheck.js and the references on two pages were updated. The idea that tracking protection blocked the filename is a hypothesis. Nothing in the record shows the blocking rule itself.
I was building a video-sync web app for dance practice. Each dancer has the choreography video on their own phone, and the app plays it in step with the leader's controls. To avoid starting a server on a laptop every time, I moved the sync signaling to the cloud. After that, picking a video in Android Whale no longer enabled the Create Room and Join buttons. Regular Chrome on the same phone worked fine.
Conclusion
This Whale (Android) failure was resolved by renaming fingerprint.js to mediacheck.js and updating the references on two pages. After deployment, the report came back that it worked.
Only part of the cause is confirmed. Here is what is confirmed next to what is still unknown.
| Confirmed (backed by the record) | Unconfirmed (no record) |
|---|---|
Right after page load, window.FormationFingerprint was undefined, so destructuring fingerprintVideo aborted | Why that object was never created |
| The analysis at the time was that this init failure kept the later file-event handling and WebSocket connection code from running | Whether the script was blocked at download, or downloaded but failed to execute |
| After the rename, it worked in Whale. On desktop, the object and function were both confirmed present | The claim that Whale's tracking protection blocked the name fingerprint.js was only the leading hypothesis of the analysis at the time. The feature or rule that did the blocking was never identified |
So what you get from this post is a remedy ("renaming it fixed it this time"), not an answer about how Whale blocks things. It also doesn't mean the remedy will work in every environment that shows the same symptom.
The snippet below shows the direction of the change. The record doesn't hold the full set of tag changes, so this is not the actual code copied as-is.
<!-- before -->
<script src="fingerprint.js"></script>
<!-- after -->
<script src="mediacheck.js"></script>
The diagnostic order to follow from this record:
- Don't look only at what happens after a file is selected. Check for errors right after the page loads first. Here, the
?debug=1on-screen panel showed the destructuring error before any file was picked. - Identify the object the error points to and the script that is supposed to set it up. In this case,
window.FormationFingerprintwas missing. - To test the filename-blocking hypothesis, the script was renamed to
mediacheck.jsand the references on two pages were updated. After deployment, the report came back that it worked.
The file-picker filter (extension list) was restored at the same time. There is no record confirming that it brings up the intended picker on a real Whale device.
Situation
The leader creates a practice room and selects a video. Participants then join their room from the room list and each picks the video file they prepared. The videos are shared separately. The app was designed only to relay sync signals so playback follows the leader's controls.
To check that everyone picked the same video, the app compares a "video fingerprint," an identifier computed from part of the file. The shared script that computes it was fingerprint.js, and the pages called functions on the window.FormationFingerprint object that this script sets up. I was chasing the buttons that wouldn't turn on after video selection, and along the way I found an earlier failure: the shared script never initialized.
Symptom
On Android, the video's filename showed up, but the Create Room and Join buttons stayed disabled. The viewer sat in a "loading room info" state, while iPhone and desktop worked. Later, a comparison on the same Android phone showed that regular Chrome worked and Whale didn't.
The first report came on August 25, and on August 30 there was another report that things still couldn't proceed. Once an on-screen debug panel was added, the key lines of the log were these. The environment was Whale 3.9.14.9 (based on Chrome 138) on Android 10. The deployment URL that followed the error has been omitted.
secureContext: true | crypto.subtle: true | Blob.arrayBuffer: true | File.text: true
❌ window.error: Uncaught TypeError: Cannot destructure property 'fingerprintVideo' of 'window.FormationFingerprint' as it is undefined.
Cause
The log confirms two things: whether the listed APIs are supported, and that the object was missing at the moment of destructuring. A "supported" flag alone doesn't prove that the crypto or file-read calls actually work. And a missing object alone can't tell you whether the download was blocked or execution failed.
At the time, I also suspected that relative paths might resolve wrong on /leader, which has no .html. But opening the same path on desktop showed the object was fine, so that path alone was hard to accept as the explanation for Whale's failure. The file was then renamed on the filename-blocking hypothesis, and the report came back that it worked. No experimental result identifies the blocking feature itself.
Fix
fingerprint.js was renamed to mediacheck.js, the references on two pages were updated, and the change was deployed. The reason for the rename was also written into the file's header comment. On desktop, I confirmed that the new file loaded and that the existing FormationFingerprint object and its function were present.
Next came the reply "It works!! It worked! Nice job!" along with a request to put the file-picker filter back. At the end, the extension filter was reported as restored and deployed. There was no follow-up check on whether the restored filter brings up the intended screen in Whale. At this point the agent reinterpreted the earlier "failure" of the extension filter as also caused by the script block. The record has nothing that verified this cause separately for each filter attempt, so it remains an interpretation. The success of the rename and the verification status of the filter restore are separate questions.
To prevent a repeat, it's better to show a clear error when a required object is missing and to stop the buttons and logic that depend on it. An example that just sets the function to null and carries on with initialization doesn't protect the later call sites, so it isn't shown here. This is an extra design suggestion, not something that was applied this time.
Reporter's Notes — it had already stalled before any file was picked
This scene is reconstructed from the hypotheses and observations in the record, as four people debugging it together.
readDuration reading the video length, and it's probably waiting there. It stalls if it never gets the loadedmetadata signal saying basic info like duration is ready. accept value that restricts file types. Let's drop video/*, which means every video type, and switch to specific formats. .json? ?debug=1 and you can follow file selection, the duration read, and the fingerprint calculation, the identifier used to check it's the same video. window.FormationFingerprint is undefined. What couldn't it find? fingerprintVideo, the video fingerprint function, doesn't exist. It stopped at the destructuring step that pulls the function out of that object. /leader without .html? Maybe the location got mixed up when it looked for the script by relative path. fingerprint.js itself is being blocked instead. mediacheck.js, updated the references on both pages, and deployed. On desktop I confirmed the new file, the existing object, and the function, so open it again in Whale.