# Sources Listen Command Reference

<p>This page is a detailed reference for the <code>rudder-cli sources</code> commands. <code>sources listen</code> runs a local server that accepts events the way RudderStack ingestion does and prints each accepted event on stdout. <code>sources events</code> reads the events a listener keeps in memory.</p>
<p>For a step-by-step walkthrough with examples, see <a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/verify-events-locally/" >How to Verify App Events Locally</a>.</p>

<blockquote class="warning">
  <div class="tip-quote">
    
    <div class="tip-text"><p><strong>Experimental feature</strong></p>
<p>The <code>sources</code> commands are experimental and disabled by default. You must <a href="#enable-the-experimental-flag" >enable experimental mode and the <code>sourcesListen</code> flag</a> before you run them. Experimental features are functional but their commands, flags, and output can change or be removed in future releases, so avoid relying on them in production workflows.</p>
</div>
  </div>
</blockquote>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="overview">Overview</h2><table>
  <thead>
      <tr>
          <th>Command</th>
          <th>Use case</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>rudder-cli sources listen</code></td>
          <td>Start a local server that answers RudderStack SDKs and prints each accepted event on stdout.</td>
      </tr>
      <tr>
          <td><code>rudder-cli sources events</code></td>
          <td>Read the events a listener started with <code>--store</code> keeps in memory.</td>
      </tr>
  </tbody>
</table>
<p>The listener needs no access token, no workspace, and no network. It does not deliver events to destinations, run transformations, or validate events against a Tracking Plan.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="enable-the-experimental-flag">Enable the experimental flag</h2><p>The <code>sources</code> commands require <strong>two</strong> settings:</p>
<ol>
<li><strong>Experimental mode</strong> (<code>experimental</code>): The main switch for all experimental features.</li>
<li><strong>The <code>sourcesListen</code> flag</strong>: Turns on the <code>sources</code> commands.</li>
</ol>
<p>If either is disabled, every <code>sources</code> command exits with code <code>1</code> and the error code <code>experimental_disabled</code>. The message names both environment variables.</p>
<div class="rs-tabs">
	<div class="rs-tabs__list">
		<button type="button" class="rs-tabs__tab"
			id="tab-fcbdae"
		
		>Config file</button>
		<button type="button" class="rs-tabs__tab"
			id="tab-dcfbea"
		
		>CLI commands</button>
		<button type="button" class="rs-tabs__tab"
			id="tab-fbcaed"
		
		>Environment variables</button>
	</div>
	
<div class="rs-tabs__panel" id="panel-fcbdae">

<p>Edit your Rudder CLI configuration file (<code>~/.rudder/config.json</code>):</p>
<div class="rs-code">
  <div class="rs-code__head">json<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-json" data-lang="json">{
  &#34;experimental&#34;: true,
  &#34;flags&#34;: {
    &#34;sourcesListen&#34;: true
  }
}</code></pre></div>
</div>

</div>

<div class="rs-tabs__panel" id="panel-dcfbea" hidden>

<p>Experimental mode must be on before the <code>experimental</code> subcommands run. Export <code>RUDDERSTACK_CLI_EXPERIMENTAL=true</code>, then enable the flag:</p>
<div class="rs-code">
  <div class="rs-code__head">shell<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">rudder-cli experimental enable sourcesListen
rudder-cli experimental list</code></pre></div>
</div>

</div>

<div class="rs-tabs__panel" id="panel-fbcaed" hidden>

<p>Use environment variables in CI pipelines, agent shells, and other non-interactive shells:</p>
<div class="rs-code">
  <div class="rs-code__head">shell<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">export RUDDERSTACK_CLI_EXPERIMENTAL=true
export RUDDERSTACK_X_SOURCES_LISTEN=true</code></pre></div>
</div>

</div>

</div>

<html lang="en">
<blockquote class="info">
  <div class="tip-quote">
    
    <div class="tip-text"><code>rudder-cli sources --help</code> prints the full user guide for these commands, and does not need the flag.</div>
  </div>
</blockquote>

</html>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="start-a-listener">Start a listener</h2><!-- end-chunk -->
<!-- begin-chunk -->
<h3 id="command-syntax">Command syntax</h3><div class="rs-code">
  <div class="rs-code__head">bash<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">rudder-cli sources listen [flags]</code></pre></div>
</div>
<!-- end-chunk -->
<!-- begin-chunk -->
<h3 id="parameters">Parameters</h3><table>
  <thead>
      <tr>
          <th>Parameter</th>
          <th>Type</th>
          <th>Description</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>--port</code></td>
          <td>Integer</td>
          <td>TCP port to listen on. <code>0</code>, the default, lets the operating system pick a free port, so parallel listeners never collide. A fixed port that is taken fails at start with <code>port_in_use</code>.</td>
      </tr>
      <tr>
          <td><code>--bind</code></td>
          <td>String</td>
          <td>IP address to listen on. Default: <code>127.0.0.1</code>. Use <code>0.0.0.0</code> only inside a container. A non-loopback bind prints a warning on stderr.</td>
      </tr>
      <tr>
          <td><code>--write-key</code></td>
          <td>String</td>
          <td>Accept only this write key and answer <code>401</code> to every other key and to a request without one on the ingestion routes. <br /><br />On <code>/sourceConfig</code>, a missing key gets <code>401</code> and a key that is not allowed gets <code>400</code>. <br /><br />Repeat the flag for more keys. Without it, the listener accepts any key, including a missing one.</td>
      </tr>
      <tr>
          <td><code>-s</code>, <code>--store</code></td>
          <td>Flag</td>
          <td>Also keep every request in memory, and serve the <a href="#query-api" >query API</a> and the <a href="#review-page" >review page</a>.</td>
      </tr>
      <tr>
          <td><code>--allow-host</code></td>
          <td>String</td>
          <td>With <code>--store</code>, also accept this <code>Host</code> name on the query API, such as a container service name. Repeat the flag for more names.</td>
      </tr>
      <tr>
          <td><code>-h</code>, <code>--help</code></td>
          <td>Flag</td>
          <td>Displays help information for the command.</td>
      </tr>
  </tbody>
</table>
<!-- end-chunk -->
<!-- begin-chunk -->
<h3 id="description">Description</h3><p><code>sources listen</code> runs in the foreground until it receives <code>SIGINT</code> (Ctrl+C) or <code>SIGTERM</code>. It then finishes the requests in progress and exits with code <code>0</code>. To run it in the background, use your shell&rsquo;s <code>&amp;</code> and stop it with <code>kill &lt;pid&gt;</code>.</p>
<p>The listener answers every route an SDK calls:</p>
<ul>
<li>The ingestion routes <code>/v1/track</code>, <code>/v1/identify</code>, <code>/v1/page</code>, <code>/v1/screen</code>, <code>/v1/group</code>, <code>/v1/alias</code>, <code>/v1/batch</code>, <code>/v1/merge</code>, <code>/v1/import</code>, <code>/v1/audiencelist</code>, <code>/beacon/v1/batch</code>, <code>/pixel/v1/track</code>, and <code>/pixel/v1/page</code>, with RudderStack&rsquo;s status codes and error messages.</li>
<li><code>/sourceConfig</code>, with a fixed source configuration that has no destinations, so browser and mobile SDKs finish loading.</li>
<li>CORS preflights and the health routes.</li>
</ul>
<!-- end-chunk -->
<!-- begin-chunk -->
<h3 id="output">Output</h3><p>The listener writes two streams:</p>
<table>
  <thead>
      <tr>
          <th>Stream</th>
          <th>Content</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>stdout</td>
          <td>Each accepted event as one JSON line, exactly as the SDK sent it, with whitespace removed. A batch of five events gives five lines. Nothing is added: no sequence number, no receive time, no <code>messageId</code> the SDK did not send. Configuration calls, preflights, health checks, and refused requests write nothing to stdout.</td>
      </tr>
      <tr>
          <td>stderr</td>
          <td>First, the ready line. Then one <code>refused: STATUS METHOD ROUTE: REASON</code> line for each refused ingestion request. A startup error is the last line, as one JSON object.</td>
      </tr>
  </tbody>
</table>
<p>The <strong>ready line</strong> is one JSON object that the listener prints once the port accepts connections:</p>
<div class="rs-code">
  <div class="rs-code__head">json<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-json" data-lang="json">{&#34;ready&#34;:true,&#34;apiVersion&#34;:&#34;v1&#34;,&#34;serverId&#34;:&#34;225d1f4623d83972&#34;,&#34;url&#34;:&#34;http://127.0.0.1:4321&#34;,&#34;port&#34;:4321,&#34;bind&#34;:&#34;127.0.0.1&#34;,&#34;pid&#34;:66751,&#34;startedAt&#34;:&#34;2026-10-14T17:14:57Z&#34;,&#34;writeKey&#34;:&#34;dev&#34;,&#34;writeKeyPolicy&#34;:&#34;any&#34;}</code></pre></div>
</div>
<table>
  <thead>
      <tr>
          <th>Field</th>
          <th>Description</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>url</code></td>
          <td>The base URL to give the SDK as its data plane URL (and config URL).</td>
      </tr>
      <tr>
          <td><code>port</code>, <code>bind</code>, <code>pid</code></td>
          <td>The port, the bound address, and the process ID to stop.</td>
      </tr>
      <tr>
          <td><code>serverId</code></td>
          <td>A random ID for this run. A restarted listener has a new one.</td>
      </tr>
      <tr>
          <td><code>writeKey</code>, <code>writeKeyPolicy</code></td>
          <td><code>dev</code> and <code>any</code> by default. With <code>--write-key</code>, the first allowed key and <code>allowlist</code>. A key longer than 8 characters appears masked.</td>
      </tr>
      <tr>
          <td><code>cursor</code>, <code>ui</code></td>
          <td>Only with <code>--store</code>: the current position in the store, and the review page URL.</td>
      </tr>
  </tbody>
</table>
<p>A <strong>refused line</strong> looks like this:</p>
<div class="rs-code">
  <div class="rs-code__head"><button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">refused: 400 POST /v1/track: invalid json
refused: 401 POST /v1/batch: invalid write key
refused: 401 POST /v1/track: failed to read writekey from header</code></pre></div>
</div>
<p>A pixel request answers <code>200</code> even when its event is dropped. It still prints a <code>refused:</code> line.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h3 id="examples">Examples</h3><ul>
<li>Print events as they arrive:</li>
</ul>
<div class="rs-code">
  <div class="rs-code__head">bash<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">rudder-cli sources listen --port 4321</code></pre></div>
</div>
<ul>
<li>Show only one event name:</li>
</ul>
<div class="rs-code">
  <div class="rs-code__head">bash<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">rudder-cli sources listen --port 4321 | grep &#39;&#34;event&#34;:&#34;Order Completed&#34;&#39;</code></pre></div>
</div>
<ul>
<li>Run in the background, with the events and the log in files:</li>
</ul>
<div class="rs-code">
  <div class="rs-code__head">bash<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">rudder-cli sources listen --port 0 &gt; events.ndjson 2&gt; listen.log &amp;</code></pre></div>
</div>
<ul>
<li>Accept only two write keys:</li>
</ul>
<div class="rs-code">
  <div class="rs-code__head">bash<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">rudder-cli sources listen --port 4321 --write-key web-key --write-key api-key</code></pre></div>
</div>
<ul>
<li>Keep requests in memory for <code>sources events</code> and the review page:</li>
</ul>
<div class="rs-code">
  <div class="rs-code__head">bash<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">rudder-cli sources listen --port 4321 --store</code></pre></div>
</div>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="read-stored-events">Read stored events</h2><!-- end-chunk -->
<!-- begin-chunk -->
<h3 id="command-syntax-1">Command syntax</h3><div class="rs-code">
  <div class="rs-code__head">bash<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">rudder-cli sources events --url &lt;listener-url&gt; [flags]</code></pre></div>
</div>
<!-- end-chunk -->
<!-- begin-chunk -->
<h3 id="parameters-1">Parameters</h3><table>
  <thead>
      <tr>
          <th>Parameter</th>
          <th>Type</th>
          <th>Description</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>--url</code></td>
          <td>String</td>
          <td>The listener URL from the ready line. Default: the <code>RUDDERSTACK_DEV_URL</code> environment variable.</td>
      </tr>
      <tr>
          <td><code>--since</code></td>
          <td>String</td>
          <td>Read after this cursor, since a duration such as <code>5m</code>, or since an RFC 3339 time. Default: <code>0</code>.</td>
      </tr>
      <tr>
          <td><code>--event</code></td>
          <td>String</td>
          <td>Only events with this name, or a prefix that ends in <code>*</code> such as <code>'Order*'</code>. Repeat the flag for more names.</td>
      </tr>
      <tr>
          <td><code>--type</code></td>
          <td>String</td>
          <td>Only events of this type, such as <code>track</code> or <code>page</code>. Repeat the flag for more types.</td>
      </tr>
      <tr>
          <td><code>--user-id</code></td>
          <td>String</td>
          <td>Only events with this <code>userId</code>.</td>
      </tr>
      <tr>
          <td><code>--anonymous-id</code></td>
          <td>String</td>
          <td>Only events with this <code>anonymousId</code>.</td>
      </tr>
      <tr>
          <td><code>--write-key</code></td>
          <td>String</td>
          <td>Only requests sent with this write key. <code>''</code> selects requests without one. Repeat the flag for more keys.</td>
      </tr>
      <tr>
          <td><code>--limit</code></td>
          <td>Integer</td>
          <td>Print at most this many events, from 1 to 1000. Default: <code>100</code>. A request is never split across pages.</td>
      </tr>
      <tr>
          <td><code>--view</code></td>
          <td>String</td>
          <td><code>list</code> (a table of whole events, the default), <code>compact</code> (without the context the SDK collects on its own), or <code>full</code>.</td>
      </tr>
      <tr>
          <td><code>--fields</code></td>
          <td>String</td>
          <td>Keep only this dotted path of each event, such as <code>properties</code>. Repeat the flag for more paths. Cannot be combined with <code>--view</code>.</td>
      </tr>
      <tr>
          <td><code>--server-id</code></td>
          <td>String</td>
          <td>Fail with <code>server_changed</code> unless the listener has this <code>serverId</code>.</td>
      </tr>
      <tr>
          <td><code>--timeout</code></td>
          <td>Duration</td>
          <td>Give up on the listener after this long. Default: <code>5s</code>.</td>
      </tr>
      <tr>
          <td><code>-j</code>, <code>--json</code></td>
          <td>Flag</td>
          <td>Print NDJSON, one event per line, instead of a table.</td>
      </tr>
      <tr>
          <td><code>-h</code>, <code>--help</code></td>
          <td>Flag</td>
          <td>Displays help information for the command.</td>
      </tr>
  </tbody>
</table>
<p>Different flags combine with AND. A repeated flag combines with OR.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h3 id="description-1">Description</h3><p><code>sources events</code> reads from a listener started with <code>--store</code>. Without <code>--store</code>, it fails with <code>store_disabled</code>. With <code>--json</code>, each line is an accepted event exactly as the SDK sent it, the same lines the listener prints on stdout. When more events are left than <code>--limit</code> allows, stderr prints <code>more events: continue with --since N</code>, and stdout stays pure.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h3 id="examples-1">Examples</h3><ul>
<li>The properties of one event:</li>
</ul>
<div class="rs-code">
  <div class="rs-code__head">bash<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">rudder-cli sources events --url http://127.0.0.1:4321 --event &#39;Order Completed&#39; --fields properties --json</code></pre></div>
</div>
<ul>
<li>A table of what arrived in the last five minutes:</li>
</ul>
<div class="rs-code">
  <div class="rs-code__head">bash<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">rudder-cli sources events --url http://127.0.0.1:4321 --since 5m</code></pre></div>
</div>
<ul>
<li>Events without the auto-collected context:</li>
</ul>
<div class="rs-code">
  <div class="rs-code__head">bash<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">rudder-cli sources events --url http://127.0.0.1:4321 --view compact --json</code></pre></div>
</div>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="query-api">Query API</h2><p>A listener started with <code>--store</code> serves a read-only HTTP API on the same port, under <code>/_dev/v1/</code>. Use it from a test process in any language.</p>
<table>
  <thead>
      <tr>
          <th>Route</th>
          <th>Description</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>GET /_dev/v1/</code></td>
          <td>The index: every route with its parameters and an example URL.</td>
      </tr>
      <tr>
          <td><code>GET /_dev/v1/info</code></td>
          <td>The listener&rsquo;s identity, current cursor, and store statistics. Use it as a readiness probe.</td>
      </tr>
      <tr>
          <td><code>GET /_dev/v1/events</code></td>
          <td>The stored accepted events as NDJSON. It takes the same parameters as <code>sources events</code>: <code>since</code>, <code>event</code>, <code>type</code>, <code>userId</code>, <code>anonymousId</code>, <code>writeKey</code>, <code>limit</code>, <code>view</code>, <code>fields</code>, <code>serverId</code>.</td>
      </tr>
      <tr>
          <td><code>GET /_dev/v1/requests</code></td>
          <td>The request records after <code>since</code>: status, outcome, the reason a request was refused, six request headers, the raw body, and the response. <br /><br />Filter with <code>kind</code> (<code>ingestion</code> by default, <code>control</code>, or <code>all</code>), <code>statusCode</code> (such as <code>401</code> or <code>4xx</code>), <code>failed</code>, <code>writeKey</code>, <code>messageId</code>, and <code>serverId</code>. <br /><br />Shape the page with <code>limit</code> (0 to 1,000, default <code>100</code>), <code>view</code> (<code>list</code> by default, <code>compact</code>, or <code>full</code>), <code>fields</code>, and <code>maxBytes</code> (default 24,000 bytes; <code>0</code> turns the cap off). When <code>maxBytes</code> cuts the page, <code>truncated.next</code> is the command that reads the rest.</td>
      </tr>
      <tr>
          <td><code>GET /_dev/v1/requests/{seq}</code></td>
          <td>One request record.</td>
      </tr>
      <tr>
          <td><code>GET /_dev/v1/guide</code></td>
          <td>The user guide that <code>rudder-cli sources --help</code> prints.</td>
      </tr>
      <tr>
          <td><code>GET /_dev/v1/events?view=counts</code></td>
          <td>Counts of the requests and events after <code>since</code>, and a diagnosis of why events are missing: codes such as <code>nothing_received</code>, <code>filtered_empty</code>, <code>preflight_only</code>, <code>sdk_config_rejected</code>, <code>sdk_loaded_no_events</code>, and <code>all_accepted</code>, each with a <code>next</code> command. <br /><br />The counts follow the event filters. The diagnosis reads every request after <code>since</code>, so a filter that matches nothing shows as <code>filtered_empty</code>, not as <code>nothing_received</code>. <br /><br />Add <code>wait</code> (up to <code>110s</code>) and <code>min</code> to hold the answer until at least <code>min</code> matching events arrive. <code>limit</code> and <code>fields</code> do not apply.</td>
      </tr>
  </tbody>
</table>
<p>Every <code>GET</code> route also answers <code>HEAD</code>.</p>
<p>The query API has no authentication. It accepts only requests whose <code>Host</code> is <code>localhost</code>, an address in <code>127.0.0.0/8</code>, the bound IP, or a name passed with <code>--allow-host</code>. It sends no CORS headers and refuses cross-site browser requests, so a web page cannot read captured events. Call it from a shell or a test process, never from inside the page under test.</p>
<p>Records never keep <code>Authorization</code> or cookie headers. A write key longer than 8 characters appears masked.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="review-page">Review page</h2><p>A listener started with <code>--store</code> serves a read-only web page at the <code>ui</code> URL from the ready line. It shows the captured events and requests, filters by event name and write key, searches payload values, and explains why events are missing. It loads nothing from the network.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="limits-and-differences-from-rudderstack">Limits and differences from RudderStack</h2><p>A passing check is strong evidence that the app instruments events correctly, not a guarantee. The listener answers with RudderStack&rsquo;s status codes and messages, and adds limits of its own:</p>
<ul>
<li>A request body is at most 2,048,000 bytes, raw and decoded, and a batch has at most 10,000 events. Larger requests get <code>413</code>.</li>
<li>At most 8 requests are read at once. More get <code>503</code>. A body that takes over 10 seconds to arrive is refused.</li>
<li>With <code>--store</code>, the listener keeps the last 10,000 requests or 64 MiB, and drops the oldest whole requests first.</li>
<li>It does not check the 32 KB per-event limit or the JSON depth limit.</li>
<li>It does not deliver events, run transformations, or validate events against a Tracking Plan.</li>
<li>If a reader stops reading stdout, the SDK&rsquo;s request waits until it reads again.</li>
</ul>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="error-codes">Error codes</h2><p>Errors print on stderr with a <code>Next:</code> command that fixes them. With <code>--json</code>, an error is one JSON object with <code>error.code</code> and <code>error.next</code>. Every command exits with code <code>0</code> on success and <code>1</code> on an error.</p>
<table>
  <thead>
      <tr>
          <th>Code</th>
          <th>Cause</th>
          <th>Fix</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>experimental_disabled</code></td>
          <td>Experimental mode or the <code>sourcesListen</code> flag is off.</td>
          <td><a href="#enable-the-experimental-flag" >Enable the experimental flag</a>.</td>
      </tr>
      <tr>
          <td><code>port_in_use</code></td>
          <td>The fixed <code>--port</code> is taken.</td>
          <td>Use another port, or <code>--port 0</code>.</td>
      </tr>
      <tr>
          <td><code>store_disabled</code></td>
          <td><code>sources events</code> or the query API reached a listener started without <code>--store</code>.</td>
          <td>Restart the listener with <code>--store</code>, or read its stdout.</td>
      </tr>
      <tr>
          <td><code>server_unreachable</code></td>
          <td>No listener answers at <code>--url</code>, or something else answered there.</td>
          <td>Check the <code>url</code> in the ready line.</td>
      </tr>
      <tr>
          <td><code>server_changed</code></td>
          <td>Another or a restarted listener answers at <code>--url</code>, and you passed <code>--server-id</code>.</td>
          <td>Read the new ready line and repeat the action.</td>
      </tr>
      <tr>
          <td><code>usage</code></td>
          <td>A bad flag, value, or combination, such as <code>--limit 0</code> or <code>--fields</code> with <code>--view</code>.</td>
          <td>Run the command with <code>--help</code>.</td>
      </tr>
      <tr>
          <td><code>host_not_allowed</code></td>
          <td>A query API request with a <code>Host</code> the listener does not accept.</td>
          <td>Call it as <code>127.0.0.1:&lt;port&gt;</code> or <code>localhost:&lt;port&gt;</code>, or start the listener with <code>--allow-host &lt;name&gt;</code>.</td>
      </tr>
      <tr>
          <td><code>browser_origin</code></td>
          <td>A web page on another site called the query API.</td>
          <td>Call the API from a shell or a test process.</td>
      </tr>
      <tr>
          <td><code>listen_failed</code></td>
          <td>The listener could not listen on the address, or stopped serving, for a reason other than a taken port. For example, the <code>--bind</code> IP is not on this machine.</td>
          <td>Use a <code>--bind</code> address the machine owns, such as <code>127.0.0.1</code>.</td>
      </tr>
  </tbody>
</table>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="see-more">See more</h2><ul>
<li><a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/verify-events-locally/" >How to Verify App Events Locally</a>: The local loop, SDK setup, agents, and CI recipes.</li>
<li><a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/installation/" >Install Rudder CLI</a>: Install the CLI and its Docker image.</li>
<li><a href="https://www.rudderstack.com/docs/monitor/live-events/" >Live Events</a>: Check the events a workspace source receives after you deploy.</li>
</ul>

