<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Daemon on Nick Liu - Software Engineer</title>
    <link>/tags/daemon/</link>
    <description>Recent content in Daemon on Nick Liu - Software Engineer</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <managingEditor>nickboy@users.noreply.github.com (Nick Liu)</managingEditor>
    <webMaster>nickboy@users.noreply.github.com (Nick Liu)</webMaster>
    <copyright>2026 Nick Liu</copyright>
    <lastBuildDate>Sun, 10 May 2026 22:48:37 -0700</lastBuildDate><atom:link href="/tags/daemon/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>launchctl unload returned 0. The daemon was still running. KeepAlive raced.</title>
      <link>/posts/launchd-bootstrap-debugging/</link>
      <pubDate>Sun, 10 May 2026 00:00:00 +0000</pubDate>
      <author>nickboy@users.noreply.github.com (Nick Liu)</author>
      <guid>/posts/launchd-bootstrap-debugging/</guid>
      <description>&lt;div class=&#34;lead text-neutral-500 dark:text-neutral-400 !mb-9 text-xl&#34;&gt;&#xA;  &#xA;`launchctl unload ~/Library/LaunchAgents/com.nickboy.claudedeck.plist` exited 0. Then `pgrep -f claudedeck-daemon` printed a fresh PID. Three seconds after the &#34;unload succeeded&#34; line. Spoiler: KeepAlive is a polling supervisor, not an event-driven one, and when you tell launchd to tear a job down, there is a window where the supervisor has already noticed the previous PID is gone and started a replacement.&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;p&gt;(One-paragraph grounding if launchd isn&amp;rsquo;t your daily driver: &lt;em&gt;launchd&lt;/em&gt; is macOS&amp;rsquo;s init system — the equivalent of &lt;code&gt;systemd&lt;/code&gt; on Linux or Windows Services on Windows. It boots PID 1, brings up daemons, restarts them when they crash. A &lt;em&gt;LaunchAgent&lt;/em&gt; is a per-user launchd job, defined by an XML &lt;em&gt;plist&lt;/em&gt; — property list — at &lt;code&gt;~/Library/LaunchAgents/&amp;lt;name&amp;gt;.plist&lt;/code&gt;. &lt;em&gt;KeepAlive&lt;/em&gt; is one of the plist keys; set it to &lt;code&gt;true&lt;/code&gt; and launchd will respawn the job whenever it exits. &lt;em&gt;&lt;code&gt;launchctl&lt;/code&gt;&lt;/em&gt; is the CLI you use to load, unload, and inspect those jobs. The Linux mental model: think &lt;code&gt;systemctl&lt;/code&gt; driving &lt;code&gt;systemd&lt;/code&gt; unit files. The Stream Deck plugin and its daemon are described in &lt;a href=&#34;/posts/tcc-cdhash-trap/&#34; &gt;the TCC cdhash trap post&lt;/a&gt; if you want the project context.)&lt;/p&gt;</description>
      
    </item>
    
  </channel>
</rss>
