<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Linxi</title>
    <description>The latest articles on DEV Community by Linxi (@linxi8590jpg).</description>
    <link>https://dev.to/linxi8590jpg</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4114917%2Fa71613a4-dc4d-48f6-9571-dd1d49cfc454.png</url>
      <title>DEV Community: Linxi</title>
      <link>https://dev.to/linxi8590jpg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/linxi8590jpg"/>
    <language>en</language>
    <item>
      <title>Three ways LLM tool calls lie to you — and the action layer I built to catch them</title>
      <dc:creator>Linxi</dc:creator>
      <pubDate>Tue, 08 Sep 2026 05:15:03 +0000</pubDate>
      <link>https://dev.to/linxi8590jpg/three-ways-llm-tool-calls-lie-to-you-and-the-action-layer-i-built-to-catch-them-2pf0</link>
      <guid>https://dev.to/linxi8590jpg/three-ways-llm-tool-calls-lie-to-you-and-the-action-layer-i-built-to-catch-them-2pf0</guid>
      <description>&lt;p&gt;When an LLM calls a tool that changes real state, there are three failure modes&lt;br&gt;
that are easy to ship and hard to notice:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The phantom success.&lt;/strong&gt; The tool returns &lt;code&gt;{"ok": true}&lt;/code&gt;, but the write never&lt;br&gt;
actually landed — a swallowed error, a fire-and-forget request, a cache that&lt;br&gt;
answered instead of the database. The model happily tells the user it's done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The double write.&lt;/strong&gt; A network hiccup or the model re-issuing a call applies&lt;br&gt;
the same write twice. Idempotency keys fix this — unless they introduce failure&lt;br&gt;
mode three.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The leaky idempotency cache.&lt;/strong&gt; If the cache returns a stored receipt &lt;em&gt;before&lt;/em&gt;&lt;br&gt;
checking who is asking, anyone who knows (or guesses) a key can read someone&lt;br&gt;
else's result. And if the key isn't bound to the request content, a retry with&lt;br&gt;
different arguments silently gets the old answer. I caught exactly this bug in an&lt;br&gt;
early version of my own code during review — auth ran after the cache lookup.&lt;/p&gt;

&lt;p&gt;agent-action-kit is a small JavaScript reference implementation of the layer I&lt;br&gt;
now put between the model and anything that writes: one core file, an offline&lt;br&gt;
demo, tests, no runtime dependencies.&lt;/p&gt;

&lt;p&gt;What it does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Actions are declared up front, with argument validation. Authorization policies
run before the idempotency lookup — including on retries.&lt;/li&gt;
&lt;li&gt;Idempotency keys are scoped to the authenticated caller. Reusing a key with a
different action or different validated arguments is rejected as a conflict,
not served as a "retry". Matching concurrent calls share one in-flight
execution within a layer instance.&lt;/li&gt;
&lt;li&gt;An optional &lt;code&gt;snapshot&lt;/code&gt; callback runs before and after the action; its
field-level diff goes into the receipt, so the caller sees what the supplied
snapshot observed before and after. You supply the state read — the library
does not automatically verify database persistence or reject a write whose
stored result differs from the requested value.&lt;/li&gt;
&lt;li&gt;Eight tests cover validation, async authorization, replay, conflicting key
reuse, and concurrent retries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Honest boundaries: the demo uses in-memory state so it runs offline; idempotency&lt;br&gt;
is process-local. Durable deduplication across workers, transactions, and&lt;br&gt;
handling a write that commits before an error are still the host application's&lt;br&gt;
job. This is a reference for the ordering and scoping rules, not a distributed&lt;br&gt;
systems library.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/linxi8590-jpg/agent-action-kit" rel="noopener noreferrer"&gt;https://github.com/linxi8590-jpg/agent-action-kit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you've hit failure modes this doesn't cover, open an issue on the repo with a&lt;br&gt;
reproducible case.&lt;/p&gt;

&lt;p&gt;Disclosure: This article was drafted and reviewed with AI assistance. I verified&lt;br&gt;
its technical claims against the linked source code and tests.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>llm</category>
      <category>node</category>
      <category>agents</category>
    </item>
  </channel>
</rss>
