When I started building Tasky, a tiny task-timer that lives in the Windows system tray fix with javascript, I hit a wall almost right away. My tray icon refused to show, and Electron kept shouting:
Uncaught Exception: TypeError: Insufficient number of arguments
Below is the full story of what went wrong, how I solved it, and what I added next so the client gets a smoother product.
Error Code
Here’s the first version of index.js
I wrote:
path = require('path');
const { app, BrowserWindow, Tray, Menu, nativeImage } = require('electron');
let mainWindow;
let tray; // I forgot this at first
app.on('ready', () => {
mainWindow = new BrowserWindow({
width: 300,
height: 500,
frame: false,
resizable: false,
webPreferences: { nodeIntegration: true }
});
mainWindow.loadURL(`file://${__dirname}/src/index.html`);
const iconName = process.platform === 'win32'
? 'windows-icon.png' // ← here’s mistake #1
: 'iconTemplate.png';
const iconPath = path.join(__dirname, 'src', 'assets', iconName);
tray = new Tray(iconPath); // mistake #2 shows up later
});
The code looked fine, yet Windows threw the “insufficient arguments” error and my tray stayed empty.
Define the Error
- Windows only accepts
.ico
files in the system tray.
I gave it a PNG, so Electron silently treated the path as invalid and passed “nothing” to theTray
constructor. Electron then complains that it got zero arguments. - Electron garbage-collects unreferenced objects.
If I didn’t keeptray
in a global variable, the icon would vanish the moment the event loop tidied up.
Fix the Basics
I replaced the PNG with a proper ICO and kept the Tray
object alive. This is the repaired snippet:
iconName = process.platform === 'win32' ? 'windows-icon.ico'
: 'iconTemplate.png';
const iconPath = path.join(__dirname, 'src', 'assets', iconName);
const icon = nativeImage.createFromPath(iconPath);
tray = new Tray(icon); // now Electron gets the image it wants
tray.setToolTip('Tasky – running in tray');
That alone removed the exception and showed the little stopwatch icon right where it belonged.
Correct Code
A working icon is fine, but I wanted a bit more comfort for the user. I split the file into two helper functions and added a few tray actions.
// main.js
const path = require('path');
const { app, BrowserWindow, Tray, Menu, nativeImage } = require('electron');
let mainWindow;
let tray;
function createWindow() {
mainWindow = new BrowserWindow({
width: 300,
height: 500,
frame: false,
resizable: false,
show: false
});
mainWindow.loadURL(`file://${__dirname}/src/index.html`);
// hide instead of quit
mainWindow.on('close', (e) => {
if (!app.isQuiting) {
e.preventDefault();
mainWindow.hide();
}
});
}
function createTray() {
const iconName = process.platform === 'win32' ? 'windows-icon.ico' : 'iconTemplate.png';
const iconPath = path.join(__dirname, 'src', 'assets', iconName);
const icon = nativeImage.createFromPath(iconPath);
tray = new Tray(icon);
const menu = Menu.buildFromTemplate([
{ label: 'Show / Hide', click: () => {
if (mainWindow.isVisible()) mainWindow.hide();
else mainWindow.show();
}},
{ label: 'Reload', click: () => mainWindow.reload() },
{ type: 'separator' },
{ label: 'Quit', click: () => {
app.isQuiting = true;
app.quit();
}}
]);
tray.setToolTip('Tasky – running in tray');
tray.setContextMenu(menu);
tray.on('click', () =>
mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show()
);
}
app.whenReady().then(() => {
createWindow();
createTray();
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
Explain it
Feature | Why it helps the client |
---|---|
Hide-to-tray instead of closing | Keeps Tasky running without cluttering the taskbar |
Single-click toggle | Quick way to open and hide the window |
Context menu | Provides explicit Quit and Reload actions |
Global tray variable | Prevents the icon from disappearing |
Quick Ideas for Future Practice
- Balloon notifications when a timer finishes (
Tray.displayBalloon()
on Windows). - Persistent window size using
electron-store
. - Auto-launch at login through
app.setLoginItemSettings()
. - Global keyboard shortcut to pop the window with
globalShortcut.register()
.
Each one teaches a new Electron API and lets us extend Tasky bit by bit.
Final Thought
I went from a puzzling “insufficient arguments” error to a neat little timer that tucks itself away until you need it. The fix boiled down to two simple points use the right icon format and hold on to the Tray
object but walking through the why made the lesson stick. Next sprint, I’ll add those balloon alerts so Tasky politely tells you when time’s up without yanking you out of your flow.