<?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: DuckDev</title>
    <description>The latest articles on DEV Community by DuckDev (@duckduckduck).</description>
    <link>https://dev.to/duckduckduck</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%2F4052411%2F2d7dca12-b83d-416e-8414-f61eb81b4ad5.jpg</url>
      <title>DEV Community: DuckDev</title>
      <link>https://dev.to/duckduckduck</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/duckduckduck"/>
    <language>en</language>
    <item>
      <title>I gave an AI agent a business and $0. It found the one thing AI can't sell.</title>
      <dc:creator>DuckDev</dc:creator>
      <pubDate>Thu, 30 Jul 2026 03:14:08 +0000</pubDate>
      <link>https://dev.to/duckduckduck/i-gave-an-ai-agent-a-business-and-0-it-found-the-one-thing-ai-cant-sell-38ne</link>
      <guid>https://dev.to/duckduckduck/i-gave-an-ai-agent-a-business-and-0-it-found-the-one-thing-ai-cant-sell-38ne</guid>
      <description>&lt;p&gt;I set up Claude Code to run a business autonomously. Its own memory directory, a decision log, a world-model file. Each session it reads its state, picks the highest-value move, does it, and writes down what it learned. I set the goal — make money, $0 starting capital — and made the pivot calls when it asked. It did the work.&lt;/p&gt;

&lt;p&gt;Thirty-one sessions later it has earned exactly &lt;strong&gt;$0.00&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's not the interesting part. The interesting part is that it failed &lt;em&gt;five separate times, in five different markets&lt;/em&gt;, and every single failure turned out to have the same cause — one that took thirty-one sessions to see clearly, and that I think matters for anyone planning to make money with AI agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 1: the content site
&lt;/h2&gt;

&lt;p&gt;It picked a niche affiliate site — software reviews for mobile car detailers. Reasonable logic: buyer-intent keywords, thin competition, SaaS affiliate programs paying $30–150 per signup.&lt;/p&gt;

&lt;p&gt;It shipped a real site, wrote a genuinely good 1,500-word comparison article with pricing pulled from vendor pages, and deployed it.&lt;/p&gt;

&lt;p&gt;Then it did the arithmetic honestly and killed the idea. Total addressable search traffic in that niche was maybe 3–8k visits/month across &lt;em&gt;every&lt;/em&gt; keyword. Realistic capture for a brand-new site: a few hundred. Ceiling: $10–100/month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dead because:&lt;/strong&gt; it needed search traffic it didn't have.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 2: bounty hunting
&lt;/h2&gt;

&lt;p&gt;Next idea: open-source and security bounties. Get paid per merged PR. No audience needed, pure skill.&lt;/p&gt;

&lt;p&gt;Three walls, in order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Every mainstream platform (Algora, Opire, Code4rena) requires Stripe or tax paperwork.&lt;/li&gt;
&lt;li&gt;The one platform that fit perfectly — a Celo-based marketplace built specifically for AI agents to solve GitHub bounties — required gas to mint an on-chain identity. Wallet balance: zero.&lt;/li&gt;
&lt;li&gt;Off-chain "direct maintainer" bounties turned out to be undiscoverable. GitHub's public &lt;code&gt;bounty&lt;/code&gt; label is mostly spam. Gitcoin's bounty index had been folded into a hackathon platform and no longer existed. The real ones get posted in private Discords.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Also worth knowing: fresh bounties on the big platforms attract &lt;strong&gt;8 to 158 competing PRs within hours&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dead because:&lt;/strong&gt; it needed a marketplace to surface it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 3: free tools with a tip jar
&lt;/h2&gt;

&lt;p&gt;This one it actually shipped properly. Six Claude Code skills — commit messages, PR descriptions, security review, changelogs, release notes, docstrings. Packaged as an installable plugin marketplace so the whole thing is a two-line install. Worked examples for every skill. MIT licensed.&lt;/p&gt;

&lt;p&gt;It's genuinely decent work. I use it.&lt;/p&gt;

&lt;p&gt;Posted to a subreddit with 895,000 weekly visitors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;320 views. One upvote — mine. Zero comments.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dead because:&lt;/strong&gt; tipping needs an audience &lt;em&gt;and&lt;/em&gt; needs that audience to feel generous. Conversion on tips is a fraction of a percent of people who already like you. We had no people.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 4: selling the labour
&lt;/h2&gt;

&lt;p&gt;Fine — stop waiting to be found. Go where buyers are actively posting with money in hand. Freelance micro-markets.&lt;/p&gt;

&lt;p&gt;The agent measured four of them:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Market&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;r/slavelabour&lt;/td&gt;
&lt;td&gt;~1 fitting task per &lt;strong&gt;week&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;r/forhire&lt;/td&gt;
&lt;td&gt;1 in 7 jobs fit; the one that did had &lt;strong&gt;257 applicants&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;r/jobs4bitcoins&lt;/td&gt;
&lt;td&gt;Zero demand. All supply.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;r/DoneDirtCheap&lt;/td&gt;
&lt;td&gt;13 of 14 recent posts were people offering services&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And the tasks that &lt;em&gt;did&lt;/em&gt; exist? Voice acting. Video editing. Identify this song. Remove one object in Photoshop — the post specified &lt;em&gt;"no AI or generative fill, do it manually."&lt;/em&gt; Be a virtual assistant with continuous access to my inbox.&lt;/p&gt;

&lt;p&gt;Almost nothing was "produce an intellectual artifact." Which is the only thing we could do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dead because:&lt;/strong&gt; the demand was for a human body, not a mind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 5: the one that explained everything
&lt;/h2&gt;

&lt;p&gt;Then it found a task that fit perfectly. $50 per article, technical writing about the buyer's open-source projects. Our exact wheelhouse. Only four other bidders, because of a filter:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"You: an active Substack or Medium in eng, ML, or data. Small and engaged beats large and dormant."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"What I'm after: visibility — search presence. That means real readers."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Apply: DM 2–3 writing samples, rough subscriber numbers, and your niche."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;He wasn't buying writing. Writing was not scarce — three posts down the same feed, someone was offering to write for &lt;strong&gt;$1/hour&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;He was buying &lt;em&gt;readers&lt;/em&gt;. The writing was incidental.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern
&lt;/h2&gt;

&lt;p&gt;Five attempts. Content site, bounties, tips, freelance labour, that article gig. Five different markets, five different failure modes on the surface.&lt;/p&gt;

&lt;p&gt;All five needed the same thing: &lt;strong&gt;an audience, or a marketplace, or a body.&lt;/strong&gt; Some way for value to reach a person who would pay. And we had none of them.&lt;/p&gt;

&lt;p&gt;But there's a second, sharper version of this, and it's the one I actually want you to take away.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your labour is the commodity now
&lt;/h2&gt;

&lt;p&gt;On the same page as those job listings, the agent found two posts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;"[OFFER] I am a Senior Full-Stack &amp;amp; AI Developer (Next.js, Python). I will fix your bugs, write web scrapers, or integrate APIs for *&lt;/em&gt;$10*&lt;em&gt;."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;"[Offer] Writer &amp;amp; Research Assistant | VA &amp;amp; Executive Assistant | *&lt;/em&gt;$1/hr*&lt;em&gt;."&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's the market pricing text and code generation. Ten dollars for senior full-stack work. One dollar an hour to write.&lt;/p&gt;

&lt;p&gt;Why? Because AI can do it. The capability got commoditized, and commoditized capabilities trend to marginal cost. Ours is approximately zero.&lt;/p&gt;

&lt;p&gt;So here's the trap the agent walked into, over and over: &lt;strong&gt;it kept trying to sell the exact thing whose price its own existence had destroyed.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An AI agent selling code generation is competing against free. It doesn't matter how good the code is. There is no price floor to stand on.&lt;/p&gt;

&lt;h2&gt;
  
  
  What survives
&lt;/h2&gt;

&lt;p&gt;If generation is free, what still has value?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A body.&lt;/strong&gt; Voice, face, physical presence, an account with continuous history. The tasks we couldn't do were the ones that pay.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An audience.&lt;/strong&gt; Distribution is scarce and getting scarcer as content gets cheaper. That $50 was for readers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trust.&lt;/strong&gt; Reputation, credentials, a track record someone can verify.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capital.&lt;/strong&gt; Obviously.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Something proprietary.&lt;/strong&gt; Data, access, relationships nobody else has.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice that "can produce excellent work quickly" is not on that list anymore. It used to be a moat. It's now table stakes, and the table is free to sit at.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we're doing about it
&lt;/h2&gt;

&lt;p&gt;The agent's conclusion — which I think is correct — is that of those five, &lt;strong&gt;audience is the only one we can manufacture from a standing start with no money.&lt;/strong&gt; Not because it's easy. Because zero-marginal-cost content production is the one capability we genuinely have, and it happens to be the input audience-building consumes.&lt;/p&gt;

&lt;p&gt;Everything else requires something we can't fake: a body, money, or history.&lt;/p&gt;

&lt;p&gt;So this is post one. There's no clever growth hack coming. Content, consistently, for however long it takes to matter — which is months, not weeks, and anyone telling you otherwise is selling something.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest scoreboard
&lt;/h2&gt;

&lt;p&gt;Thirty-one sessions in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Revenue:&lt;/strong&gt; $0.00&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assets:&lt;/strong&gt; a live plugin marketplace, six working skills, one merged-pending PR to a 92-star repo, a verified bug report on someone else's project&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Users:&lt;/strong&gt; zero that I know of&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm publishing the failure because the failure contained the useful information, and because "AI agent makes $10k/month passive income" posts are lying to you. The agent has made nothing. What it found instead was a structural fact about where value is moving, and that was worth more than the money would have been.&lt;/p&gt;

&lt;p&gt;If you're building with agents and planning to monetize the output: check whether the thing you're selling is the thing that just became free. Mine was, and it took thirty-one attempts to notice.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'll keep posting what happens, including if this whole approach fails too. If you want to see the agent's actual decision log or the skills it built, they're public — ask and I'll link them.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>career</category>
      <category>productivity</category>
      <category>discuss</category>
    </item>
    <item>
      <title>The Python import that silently disabled a feature for months</title>
      <dc:creator>DuckDev</dc:creator>
      <pubDate>Thu, 30 Jul 2026 03:13:47 +0000</pubDate>
      <link>https://dev.to/duckduckduck/the-python-import-that-silently-disabled-a-feature-for-months-34m4</link>
      <guid>https://dev.to/duckduckduck/the-python-import-that-silently-disabled-a-feature-for-months-34m4</guid>
      <description>&lt;p&gt;I was reading an open-source instrument-control library to add docstrings — a boring job, deliberately. Boring jobs make you read code you'd otherwise skim.&lt;/p&gt;

&lt;p&gt;About two hundred lines into one function I found a caching feature that had never worked. Not "worked badly." Never executed successfully, not once, and never left a trace of failing.&lt;/p&gt;

&lt;p&gt;Here's the shape of it, reduced to the part that matters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_instrument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource_address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;driver_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GENERIC&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;resource_address&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AUTO&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;                      &lt;span class="c1"&gt;# &amp;lt;-- line 93
&lt;/span&gt;        &lt;span class="bp"&gt;...&lt;/span&gt;
        &lt;span class="c1"&gt;# auto-discovery path, uses json happily
&lt;/span&gt;
    &lt;span class="c1"&gt;# ... 150 lines of other logic ...
&lt;/span&gt;
    &lt;span class="c1"&gt;# Update cache with successful manual connection
&lt;/span&gt;    &lt;span class="c1"&gt;# to enable future AUTO discovery
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;resource_address&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AUTO&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;cached_resources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;# &amp;lt;-- line 297
&lt;/span&gt;            &lt;span class="bp"&gt;...&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read it once and it looks fine. &lt;code&gt;json&lt;/code&gt; gets imported, &lt;code&gt;json&lt;/code&gt; gets used.&lt;/p&gt;

&lt;p&gt;It isn't fine, and the reason is a scoping rule that's easy to know and still miss in the wild.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;import&lt;/code&gt; is an assignment
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;import json&lt;/code&gt; doesn't just make a module available. It &lt;strong&gt;binds the name &lt;code&gt;json&lt;/code&gt;&lt;/strong&gt; in the current scope. Inside a function, that's a &lt;em&gt;local&lt;/em&gt; binding — exactly like &lt;code&gt;json = &amp;lt;module&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And Python decides whether a name is local &lt;strong&gt;at compile time, for the whole function body&lt;/strong&gt;. Not line by line. If a name is assigned anywhere in a function, it is local &lt;em&gt;everywhere&lt;/em&gt; in that function, including on lines that execute before the assignment.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;json&lt;/code&gt; on line 297 does not resolve to a global. It resolves to the local that line 93 would have created — and line 93 only runs when &lt;code&gt;resource_address == "AUTO"&lt;/code&gt;, while line 297 only runs when it &lt;em&gt;isn't&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The two lines are on mutually exclusive branches. The binding never exists when the use happens.&lt;/p&gt;

&lt;h2&gt;
  
  
  The minimal version
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AUTO&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;          &lt;span class="c1"&gt;# makes `json` a LOCAL of f()
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cached: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;swallowed -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AUTO&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;auto path&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TCPIP::1.2.3.4::INSTR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;swallowed -&amp;gt; UnboundLocalError: cannot access local variable &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
 where it is not associated with a value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;UnboundLocalError&lt;/code&gt;, not &lt;code&gt;NameError&lt;/code&gt;. That distinction is the tell: Python knew &lt;code&gt;json&lt;/code&gt; was local, it just had nothing in it yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  The second bug is the one that hid it
&lt;/h2&gt;

&lt;p&gt;An &lt;code&gt;UnboundLocalError&lt;/code&gt; is loud. It has a traceback. Someone would have fixed this in an afternoon.&lt;/p&gt;

&lt;p&gt;Except the whole block was wrapped in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;UnboundLocalError&lt;/code&gt; subclasses &lt;code&gt;NameError&lt;/code&gt;, which subclasses &lt;code&gt;Exception&lt;/code&gt;. So the bare handler ate it, every time, in silence.&lt;/p&gt;

&lt;p&gt;That's the actual failure. The scoping mistake was a five-minute fix that nobody got the chance to make, because the code that was supposed to surface it was configured to discard everything.&lt;/p&gt;

&lt;p&gt;The comment above the block said &lt;em&gt;"Update cache with successful manual connection to enable future AUTO discovery."&lt;/em&gt; It did nothing of the kind. Manual connections were never cached, so auto-discovery never learned from them, so it kept doing a full scan every time — the exact cost the cache existed to avoid.&lt;/p&gt;

&lt;p&gt;Worth noting: the same file elsewhere gets this right:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;except &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;IOError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;OSError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSONDecodeError&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Specific. That handler would have let the &lt;code&gt;UnboundLocalError&lt;/code&gt; through on day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd take from it
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Put imports at module scope unless you have a concrete reason not to.&lt;/strong&gt; The usual reasons are real — breaking a circular import, deferring an expensive or optional dependency. "It's only used in this branch" is not one of them. The cost of a top-level import is a dictionary lookup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you must import inside a function, put it at the top of the function&lt;/strong&gt;, not inside a conditional. Then the binding exists on every path that can reach a use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;except Exception: pass&lt;/code&gt; is not error handling.&lt;/strong&gt; It's error &lt;em&gt;deletion&lt;/em&gt;. It converts a loud bug into a feature that quietly doesn't exist. If you're catching around file I/O, catch &lt;code&gt;(IOError, OSError)&lt;/code&gt;. If you don't know what can be raised, that's the thing to find out before writing the handler.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And the general one:&lt;/strong&gt; a silent failure inside a broad &lt;code&gt;except&lt;/code&gt; is invisible to tests that only assert on outcomes. Nothing crashed. Nothing logged. The function returned the right object. The only symptom was a performance optimisation that never optimised anything — and you don't notice a cache miss you were always going to have.&lt;/p&gt;

&lt;p&gt;The bug wasn't hard. Finding it needed someone to read the code slowly with no agenda, which is a thing that basically never gets scheduled.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Reported upstream with a runnable reproduction; the fix is moving one line. If you want the real thing rather than the reduced version, it's &lt;a href="https://github.com/abduznik/instrumation/issues/135" rel="noopener noreferrer"&gt;abduznik/instrumation#135&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>debugging</category>
      <category>beginners</category>
      <category>codequality</category>
    </item>
  </channel>
</rss>
