Skip to content

Troubleshooting and FAQ

Prakhar Yadav edited this page Sep 16, 2026 · 1 revision

Troubleshooting and FAQ

doctor says FAIL — what to do

FAIL line Meaning Fix
fan files present & writable — fan1_manual not writable Not root, or real permission loss Re-run under sudo/pkexec. If still FAIL as root, the driver/udev state is broken — L2 is unarmed, do not enter curve/hold.
L2 fd armed — … Same root cause as above Same fix.
sensor plausibility — … ≥ Tjmax / all failed Overheating or dead coretemp Check sensors/status, cooling, kernel modules. The daemon already returned to AUTO after 3 dead polls.
fan1_min/max readback — … Driver returned garbage Check applesmc binding; doctor names the values.
config validation — invalid <key>: … (fix: …) Present-but-broken config Apply the named fix, sudo systemctl restart afanctl.
systemd unit health — Type≠notify / watchdog not armed Unit edited or not reloaded Reinstall unit, daemon-reload, restart.
Any FAIL Exit is 1 by design. Fix all FAILs; WARNs never block.

WARNs that are expected: unit not installed (until makepkg -si); running daemon predates the installed binary (restart after reinstall); hold active (release with afanctl observe); monitor_only / auto_restore_pending (see below); hwmon conversion (update the unit).

Degraded states

mode: curve (monitor-only) — the 3-strike ladder (or a 10-poll stall, or a failed startup AUTO restore) latched monitor_only. recent_errors / journal name the cause. The fan is on firmware; the daemon writes nothing. Recover: fix the cause, then re-write the command with a changed payload (freshness gate ignores byte-identical re-issues) or sudo systemctl restart afanctl.

auto_restore_pending: true — a fallback/release could not verify AUTO. The daemon retries set_mode(Auto) every poll (ERROR every 10th). Do not interpret mode: observe + verified: true here as "released" — the pending flag is the truth. Recover: fix writability, wait a poll, confirm fan1_manual reads 0 and the flag clears.

Fan hovering off-target with repeated WARNs but no degradation — by design (30-poll repeating WARN). A jittering-but-never-converging fan is reported, not degraded. Investigate cooling/dust/curve fit; re-run doctor --compare.

Config errors

Every error names the key and the fix. Common: missing key in a present file (refused, not defaulted); high >= max; max > 95; min_rpm >= max_rpm; interval_s outside 1..=12; non-integer values; unknown keys (warning only). After any edit: sudo systemctl restart afanctl (no reload), then sudo afanctl doctor.

hold rejected / clamped

Below fan1_min → exit 1, no cmd.json written (invalid hold rpm: … (fix: hold a value in 1200..=7200 rpm)). Above fan1_max → clamped. True fan-off is impossible via sysfs — off maps to hold fan1_min (hardware floor, Q2).

Layout change after a kernel bump

doctor WARNs applesmc layout … (fix: update the unit) when fan attrs appear under the hwmon husk. Fix = one-module change in smc.rs + updating the unit's ReadWritePaths; discovery already walks both layouts.

FAQ

Does afanctl work on T2 / Apple Silicon / macOS / multi-fan machines? No — pre-T2 Intel Macs only, single fan (seam kept, not built). See PRD §12.

Can it turn the fan fully off? No — fan1_min (1200 rpm) is the hardware floor via sysfs. Direct SMC key writes are out of scope.

Does the mode survive reboot? No — by design. /run is tmpfs; every boot lands in observe on the SMC curve.

Is there a man page / crates.io / AUR release? Deferred (PRD §12). Re-check the afanctl name on AUR + crates.io before first publish.

Why does non-root doctor exit 1 on a healthy machine? By design (F11): the write-mode checks open fan1_manual O_WRONLY; unwritable ⇒ L2 unarmed ⇒ real problem. Run under sudo/pkexec.

Why did curve disable itself during every ramp on early builds? The F16 defect (verify compared command vs tachometer, counting normal deceleration as failure). Fixed: verify reads back the register written (fan1_output echo, tolerance 50) inside a settle window; tracking deviations re-assert but are never counted; the stall detector keys on tach movement between polls.

once --at-temp 86 --dry-run prints SetSpeed, not EscalateMax — is the guard broken? No. The guard needs 3 consecutive polls inside one process; once is stateless. The guard is proven by the policy trace tests.

Where do I ask for auto-intervene / multi-fan / GUI? PRD §12 deferred list — not in v1. The omafan plugin consumes this CLI instead of a GUI.

Clone this wiki locally