Environment
- OS: NixOS (Linux, systemd-based)
- opencode: 1.2.26
- opencode-scheduler: latest
Problem
The scheduler plugin generates systemd timer/service units that fail immediately on NixOS with:
Failed to start opencode-job-....timer: Unit ... has a bad unit file setting.
There are three distinct bugs, all in src/index.ts:
Bug 1: Invalid OnCalendar format (line 841)
Function: cronToSystemdCalendars
The generated calendar spec includes a leading * as a day-of-week field:
OnCalendar=* *-*-* 08:00:00
systemd does not accept * as a day-of-week prefix. The correct format when no specific day is constrained is simply:
OnCalendar=*-*-* 08:00:00
systemd logs confirm:
Failed to parse calendar specification, ignoring: * *-*-* 08:00:00
Timer unit lacks value setting. Refusing.
Root cause — line 841:
calendars.push(`${dowValue} *-${monthValue}-${domValue} ${hourValue}:${minuteValue}:00`)
When dowValue is "*", it produces an invalid spec. The day-of-week prefix should only be included when it's an actual day name (Mon, Tue, etc.).
Proposed fix:
const prefix = dowValue === "*" ? "" : `${dowValue} `;
calendars.push(`${prefix}*-${monthValue}-${domValue} ${hourValue}:${minuteValue}:00`);
Bug 2: Hardcoded /usr/bin/perl (lines 1052, 1160, 1412)
The ExecStart in generated systemd service units hardcodes /usr/bin/perl:
ExecStart="/usr/bin/perl" "/home/.../.config/opencode/scheduler/supervisor.pl" "..."
On NixOS, perl lives in the Nix store (e.g., /nix/store/...-perl-5.x.y/bin/perl) or is available via /run/current-system/sw/bin/perl. /usr/bin/perl does not exist.
This affects:
createSystemdService (line 1160) — Linux
createLaunchdPlist (line 1052) — macOS
createCronEntry (line 1412) — fallback
Proposed fix: Detect the perl path dynamically at job creation time:
import { execSync } from "child_process";
function findPerl(): string {
try {
return execSync("command -v perl", { encoding: "utf-8" }).trim();
} catch {
return "/usr/bin/perl"; // fallback
}
}
Bug 3: macOS-only PATH in generated units (lines 663-673)
Function: getEnhancedPath
function getEnhancedPath(): string {
const paths = [
"/opt/homebrew/bin",
"/usr/local/bin",
"/usr/bin",
"/bin",
"/usr/sbin",
"/sbin",
]
return paths.join(":")
}
This is used as Environment="PATH=..." in the systemd service unit (line 1170). On NixOS, none of these contain any user tools — binaries are in /nix/store/..., ~/.nix-profile/bin, /run/current-system/sw/bin, etc.
Even if bug 2 were fixed to use a bare perl command, it would still fail because PATH doesn't include any NixOS paths.
Proposed fix: Capture and embed the current process.env.PATH (which contains the correct paths at plugin load time), with the hardcoded paths as fallbacks:
function getEnhancedPath(): string {
const fallbackPaths = [
"/opt/homebrew/bin",
"/usr/local/bin",
"/usr/bin",
"/bin",
"/usr/sbin",
"/sbin",
];
const currentPath = process.env.PATH || "";
if (currentPath) {
// Deduplicate: append fallbacks that aren't already present
const existing = new Set(currentPath.split(":"));
const extras = fallbackPaths.filter((p) => !existing.has(p));
return [currentPath, ...extras].join(":");
}
return fallbackPaths.join(":");
}
Workaround
For anyone hitting this on NixOS, you can manually fix the generated units after job creation:
-
Fix the timer — remove the leading * from OnCalendar:
OnCalendar=*-*-* 08:00:00
-
Fix the service — update ExecStart to use the NixOS perl path:
ExecStart=/run/current-system/sw/bin/perl ...
-
Fix PATH:
Environment="PATH=/run/current-system/sw/bin:/home/<user>/.nix-profile/bin:/nix/var/nix/profiles/default/bin:/usr/bin:/bin"
-
Reload and enable:
systemctl --user daemon-reload
systemctl --user enable --now <timer-unit>
Reproduction
# On NixOS, any schedule_job call will fail:
# opencode scheduler creates the units, then runs:
# systemctl --user start <timer>
# which immediately fails with "bad unit file setting"
Environment
Problem
The scheduler plugin generates systemd timer/service units that fail immediately on NixOS with:
There are three distinct bugs, all in
src/index.ts:Bug 1: Invalid
OnCalendarformat (line 841)Function:
cronToSystemdCalendarsThe generated calendar spec includes a leading
*as a day-of-week field:systemd does not accept
*as a day-of-week prefix. The correct format when no specific day is constrained is simply:systemd logs confirm:
Root cause — line 841:
When
dowValueis"*", it produces an invalid spec. The day-of-week prefix should only be included when it's an actual day name (Mon,Tue, etc.).Proposed fix:
Bug 2: Hardcoded
/usr/bin/perl(lines 1052, 1160, 1412)The
ExecStartin generated systemd service units hardcodes/usr/bin/perl:On NixOS, perl lives in the Nix store (e.g.,
/nix/store/...-perl-5.x.y/bin/perl) or is available via/run/current-system/sw/bin/perl./usr/bin/perldoes not exist.This affects:
createSystemdService(line 1160) — LinuxcreateLaunchdPlist(line 1052) — macOScreateCronEntry(line 1412) — fallbackProposed fix: Detect the perl path dynamically at job creation time:
Bug 3: macOS-only PATH in generated units (lines 663-673)
Function:
getEnhancedPathThis is used as
Environment="PATH=..."in the systemd service unit (line 1170). On NixOS, none of these contain any user tools — binaries are in/nix/store/...,~/.nix-profile/bin,/run/current-system/sw/bin, etc.Even if bug 2 were fixed to use a bare
perlcommand, it would still fail because PATH doesn't include any NixOS paths.Proposed fix: Capture and embed the current
process.env.PATH(which contains the correct paths at plugin load time), with the hardcoded paths as fallbacks:Workaround
For anyone hitting this on NixOS, you can manually fix the generated units after job creation:
Fix the timer — remove the leading
*fromOnCalendar:OnCalendar=*-*-* 08:00:00Fix the service — update
ExecStartto use the NixOS perl path:ExecStart=/run/current-system/sw/bin/perl ...Fix PATH:
Reload and enable:
Reproduction