<?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: SystemCraftDev</title>
    <description>The latest articles on DEV Community by SystemCraftDev (@systemcraftdev).</description>
    <link>https://dev.to/systemcraftdev</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%2F4066683%2F883dce22-d72f-4e9c-86fb-510fd7ef40a8.png</url>
      <title>DEV Community: SystemCraftDev</title>
      <link>https://dev.to/systemcraftdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/systemcraftdev"/>
    <language>en</language>
    <item>
      <title>Pipes and Redirection: How &gt;, &gt;&gt;, 2&gt; and | Really Work</title>
      <dc:creator>SystemCraftDev</dc:creator>
      <pubDate>Thu, 29 Oct 2026 02:56:48 +0000</pubDate>
      <link>https://dev.to/systemcraftdev/pipes-and-redirection-how-2-and-really-work-12m3</link>
      <guid>https://dev.to/systemcraftdev/pipes-and-redirection-how-2-and-really-work-12m3</guid>
      <description>&lt;p&gt;Four small symbols move text between commands and files. Once you see the three streams behind them, shell one-liners stop looking like magic.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Adapted from the &lt;a href="https://systemcraftpress.com/guides/command-line-essentials/?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=command-line-pipes-and-redirection" rel="noopener noreferrer"&gt;Command Line Essentials Companion Guide&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;At some point you copy a one-liner from a tutorial and it works, though you couldn't say why:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sort &lt;/span&gt;fruits.txt | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; counts.txt 2&amp;gt; errors.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It reads like line noise, but it's five small ideas in a row, and once you know them you can read almost any shell one-liner and write your own. The ideas all come from one fact: every command you run has three channels for text, and the shell lets you rewire them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three streams
&lt;/h2&gt;

&lt;p&gt;Every program starts with three connections already open:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standard input (stdin):&lt;/strong&gt; where it reads from. By default, your keyboard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standard output (stdout):&lt;/strong&gt; where its normal results go. By default, your terminal screen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standard error (stderr):&lt;/strong&gt; where its error messages go. Also your terminal screen by default.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Output and errors look identical on screen, which hides the fact that they're separate channels. That separation is what makes the rest of this work. Redirection changes where a stream points, and a pipe connects one command's output to the next command's input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending output to a file: &lt;code&gt;&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;&amp;gt;&lt;/code&gt; sends stdout to a file instead of the screen. If the file exists, it's replaced:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;one &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; out.txt
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;two &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; out.txt
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;out.txt
two
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first line was overwritten. To add to the end instead, use &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;three &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; out.txt
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;out.txt
two
three
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;&amp;gt;&lt;/code&gt; when you want a fresh file each time, and &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; for something like a log you keep adding to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending errors somewhere: &lt;code&gt;2&amp;gt;&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Errors go to stderr, which is stream number 2 (stdout is 1, stdin is 0). &lt;code&gt;&amp;gt;&lt;/code&gt; only captures stdout, so errors still appear on screen. To capture them, redirect stream 2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls &lt;/span&gt;nope fruits.txt &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; found.txt 2&amp;gt; errors.txt
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;found.txt
fruits.txt
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;errors.txt
&lt;span class="nb"&gt;ls&lt;/span&gt;: cannot access &lt;span class="s1"&gt;'nope'&lt;/span&gt;: No such file or directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file that exists went to &lt;code&gt;found.txt&lt;/code&gt;. The error for the one that doesn't went to &lt;code&gt;errors.txt&lt;/code&gt;. To throw errors away entirely, send them to &lt;code&gt;/dev/null&lt;/code&gt;, a special file that discards everything written to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls &lt;/span&gt;nope fruits.txt 2&amp;gt; /dev/null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Combining both streams: &lt;code&gt;2&amp;gt;&amp;amp;1&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;To send errors to the same place as normal output, use &lt;code&gt;2&amp;gt;&amp;amp;1&lt;/code&gt;, which reads as "point stream 2 at wherever stream 1 is pointing right now":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls &lt;/span&gt;nope fruits.txt &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; both.txt 2&amp;gt;&amp;amp;1
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;both.txt
&lt;span class="nb"&gt;ls&lt;/span&gt;: cannot access &lt;span class="s1"&gt;'nope'&lt;/span&gt;: No such file or directory
fruits.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The order matters, and this is the most common mistake with it. Redirections are applied left to right. In the version above, stdout goes to the file first, and then stderr is pointed at that same place. If you swap them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls &lt;/span&gt;nope fruits.txt 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; only.txt
&lt;span class="nb"&gt;ls&lt;/span&gt;: cannot access &lt;span class="s1"&gt;'nope'&lt;/span&gt;: No such file or directory
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;only.txt
fruits.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now stderr was pointed at the screen (where stdout was at that moment), and only afterward was stdout moved to the file. The error still lands on your screen. Put &lt;code&gt;&amp;gt; file&lt;/code&gt; first, then &lt;code&gt;2&amp;gt;&amp;amp;1&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting commands: &lt;code&gt;|&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;A pipe takes the stdout of the command on its left and feeds it to the stdin of the command on its right, with no file in between. That lets you chain small tools, each doing one job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sort fruits.txt | uniq -c | sort -rn
      3 apple
      2 banana
      1 cherry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read it left to right: sort the lines so duplicates sit together, count each run of duplicates, then sort by that count, highest first. None of these tools knows about the others. They each just read stdin and write stdout, and the pipe does the connecting.&lt;/p&gt;

&lt;p&gt;One catch: a pipe only carries stdout. Errors from the left-hand command skip the pipe and still print on screen. If you want them to go through, merge them first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls &lt;/span&gt;nope fruits.txt | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt;: cannot access &lt;span class="s1"&gt;'nope'&lt;/span&gt;: No such file or directory
1
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls &lt;/span&gt;nope fruits.txt 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Saving and viewing at once: &lt;code&gt;tee&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Redirecting to a file hides the output from your screen. &lt;code&gt;tee&lt;/code&gt; copies its input to both the screen and a file, which is useful when you want to watch a long command and keep a record:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;hi | &lt;span class="nb"&gt;tee &lt;/span&gt;t.txt
hi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Two mistakes worth knowing about ahead of time
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Redirecting a file onto itself.&lt;/strong&gt; The shell opens, and empties, the output file before the command even starts. So this wipes your data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sort &lt;/span&gt;fruits.txt &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; fruits.txt
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; fruits.txt
0 fruits.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file was emptied first, so &lt;code&gt;sort&lt;/code&gt; had nothing to read. Write to a different file, then rename it if you want to replace the original.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using &lt;code&gt;sudo&lt;/code&gt; with a redirect.&lt;/strong&gt; If you run &lt;code&gt;sudo echo text &amp;gt; /etc/somefile&lt;/code&gt; and get "Permission denied", it's because the &lt;code&gt;&amp;gt;&lt;/code&gt; is handled by your shell, which isn't running as root. Only the &lt;code&gt;echo&lt;/code&gt; got elevated, and it was never the part that needed it. Pipe into &lt;code&gt;tee&lt;/code&gt; instead, which runs under &lt;code&gt;sudo&lt;/code&gt; and does the writing: &lt;code&gt;echo text | sudo tee /etc/somefile&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A habit that makes it click
&lt;/h2&gt;

&lt;p&gt;When you see a one-liner you don't understand, read it in pieces rather than as a whole. Run the first command alone and look at what it prints. Then add the next &lt;code&gt;|&lt;/code&gt; and see what that stage changes. Because every stage just takes text in and sends text out, you can build the pipeline up one step at a time, checking each result before moving on. That's also how the people who write these one-liners build them in the first place.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you'd like more posts like this sent straight to your inbox, &lt;a href="https://buttondown.com/SystemCraftPress?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=command-line-pipes-and-redirection" rel="noopener noreferrer"&gt;subscribe to the newsletter&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Prefer to dig in yourself? The &lt;a href="https://github.com/SystemCraftPress/command-line-essentials?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=command-line-pipes-and-redirection" rel="noopener noreferrer"&gt;Command Line Essentials repo&lt;/a&gt; on GitHub has more free examples and exercises.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>bash</category>
      <category>cli</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Git's 'src refspec main does not match any', Explained</title>
      <dc:creator>SystemCraftDev</dc:creator>
      <pubDate>Thu, 22 Oct 2026 02:32:02 +0000</pubDate>
      <link>https://dev.to/systemcraftdev/gits-src-refspec-main-does-not-match-any-explained-2m1b</link>
      <guid>https://dev.to/systemcraftdev/gits-src-refspec-main-does-not-match-any-explained-2m1b</guid>
      <description>&lt;p&gt;Your push failed because the branch you named doesn't exist locally yet: no commits, a master/main mix-up, or a typo. Here's how to tell which.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Adapted from the &lt;a href="https://systemcraftpress.com/guides/git-github/?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=git-src-refspec-does-not-match-any" rel="noopener noreferrer"&gt;Git &amp;amp; GitHub Companion Guide&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;You create a new repository on GitHub, copy the commands it shows you, and run them in your project folder. The last one fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main
error: src refspec main does not match any
error: failed to push some refs to &lt;span class="s1"&gt;'https://github.com/you/your-repo.git'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The message sounds like something is wrong with GitHub, your remote, or your permissions. It isn't. Nothing was sent anywhere. Git stopped before it ever contacted the remote, because the branch you asked it to push doesn't exist on your machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's actually happening
&lt;/h2&gt;

&lt;p&gt;A push command has two parts: what to push and where to put it. In &lt;code&gt;git push origin main&lt;/code&gt;, Git treats &lt;code&gt;main&lt;/code&gt; as shorthand for "take my local &lt;code&gt;main&lt;/code&gt; and send it to &lt;code&gt;main&lt;/code&gt; on the remote." That pairing is called a refspec, and the first half, the source, is the &lt;code&gt;src&lt;/code&gt; in the error.&lt;/p&gt;

&lt;p&gt;"src refspec main does not match any" means Git looked for a local branch or commit called &lt;code&gt;main&lt;/code&gt; and found nothing. The word "any" is literal: it matches zero local references. The error is about your local repository, not the remote.&lt;/p&gt;

&lt;p&gt;There are three common reasons your local &lt;code&gt;main&lt;/code&gt; doesn't exist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. You haven't made a commit yet.&lt;/strong&gt; A branch is only a name that points at a commit. In a brand-new repository with no commits, there's nothing for the name to point at, so the branch doesn't exist yet. You can see this: &lt;code&gt;git status&lt;/code&gt; says "No commits yet", and &lt;code&gt;git branch&lt;/code&gt; prints nothing at all. Staging files with &lt;code&gt;git add&lt;/code&gt; isn't enough. Only a commit creates the branch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Your branch has a different name.&lt;/strong&gt; Older versions of Git, and many default setups, name the first branch &lt;code&gt;master&lt;/code&gt;, not &lt;code&gt;main&lt;/code&gt;. GitHub's instructions assume &lt;code&gt;main&lt;/code&gt;. If you committed on &lt;code&gt;master&lt;/code&gt; and then push &lt;code&gt;main&lt;/code&gt;, Git has no local &lt;code&gt;main&lt;/code&gt; to send.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The name is misspelled or has the wrong case.&lt;/strong&gt; Branch names are case-sensitive. &lt;code&gt;Main&lt;/code&gt;, &lt;code&gt;MAIN&lt;/code&gt; and &lt;code&gt;main&lt;/code&gt; are three different names, and &lt;code&gt;git push origin Main&lt;/code&gt; fails with the same error.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix, step by step
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Check which situation you're in.&lt;/strong&gt; Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status
git branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;git status&lt;/code&gt; shows the current branch name on its first line, and &lt;code&gt;No commits yet&lt;/code&gt; if that applies. &lt;code&gt;git branch&lt;/code&gt; lists your local branches with a &lt;code&gt;*&lt;/code&gt; next to the current one. If the list is empty, you have no commits. If it shows &lt;code&gt;master&lt;/code&gt; and you tried to push &lt;code&gt;main&lt;/code&gt;, you have a naming mismatch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. If there are no commits, make one.&lt;/strong&gt; Stage your files and commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial commit"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the branch exists. Run your push again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. If your branch is &lt;code&gt;master&lt;/code&gt; and you want &lt;code&gt;main&lt;/code&gt;, rename it.&lt;/strong&gt; The &lt;code&gt;-M&lt;/code&gt; flag renames the branch you're on, even if the new name is already taken:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git branch &lt;span class="nt"&gt;-M&lt;/span&gt; main
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you'd rather keep &lt;code&gt;master&lt;/code&gt;, push the name you actually have: &lt;code&gt;git push -u origin master&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. If it's a typo or wrong case, retype it.&lt;/strong&gt; Copy the exact name from the &lt;code&gt;git branch&lt;/code&gt; output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two mistakes worth knowing about ahead of time
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Re-running &lt;code&gt;git remote add&lt;/code&gt; while troubleshooting.&lt;/strong&gt; The remote is fine, and nothing in this error involves it. Running &lt;code&gt;git remote add origin ...&lt;/code&gt; a second time just gives you &lt;code&gt;error: remote origin already exists&lt;/code&gt;. If the URL really is wrong, &lt;code&gt;git remote -v&lt;/code&gt; shows it and &lt;code&gt;git remote set-url origin &amp;lt;url&amp;gt;&lt;/code&gt; fixes it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Confusing this with "updates were rejected".&lt;/strong&gt; They look similar because both appear after a push, but they mean different things. "src refspec does not match any" happens before contacting the remote, because your local branch is missing. "Updates were rejected" happens after contacting it, because the remote has commits you don't. If you see the second message, &lt;a href="https://systemcraftpress.com/blog/git-updates-were-rejected/" rel="noopener noreferrer"&gt;How to Fix 'Updates Were Rejected'&lt;/a&gt; covers it.&lt;/p&gt;

&lt;h2&gt;
  
  
  A habit that prevents the confusion entirely
&lt;/h2&gt;

&lt;p&gt;Two small habits remove most of this. First, set Git's default branch name once, so every new repository starts on &lt;code&gt;main&lt;/code&gt; and matches what GitHub expects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; init.defaultBranch main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second, when you're not sure what a branch is called, don't type it. &lt;code&gt;git push -u origin HEAD&lt;/code&gt; pushes whichever branch you're currently on to a branch of the same name on the remote. That sidesteps typos, case mistakes and the master/main mix-up in one command. And whenever you see "does not match any", run &lt;code&gt;git branch&lt;/code&gt; first. It takes a second and tells you whether the branch you named exists at all.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you'd like more posts like this sent straight to your inbox, &lt;a href="https://buttondown.com/SystemCraftPress?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=git-src-refspec-does-not-match-any" rel="noopener noreferrer"&gt;subscribe to the newsletter&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Prefer to dig in yourself? The &lt;a href="https://github.com/SystemCraftPress/git-github?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=git-src-refspec-does-not-match-any" rel="noopener noreferrer"&gt;Git &amp;amp; GitHub repo&lt;/a&gt; on GitHub has more free examples and exercises.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The Only VS Code Shortcuts Worth Learning First</title>
      <dc:creator>SystemCraftDev</dc:creator>
      <pubDate>Fri, 16 Oct 2026 02:45:30 +0000</pubDate>
      <link>https://dev.to/systemcraftdev/the-only-vs-code-shortcuts-worth-learning-first-154m</link>
      <guid>https://dev.to/systemcraftdev/the-only-vs-code-shortcuts-worth-learning-first-154m</guid>
      <description>&lt;p&gt;Skip the 100-shortcut cheat sheets. These seven VS Code shortcuts, in the order to learn them, cover most of your day. Start with the Command Palette.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Adapted from the &lt;a href="https://systemcraftpress.com/guides/vscode-essentials/?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=vscode-shortcuts-to-learn-first" rel="noopener noreferrer"&gt;VS Code Essentials Companion Guide&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Search for VS Code shortcuts and you'll find lists of fifty, a hundred, sometimes two hundred of them. Nobody learns a list like that. You skim it, memorize two, forget them by Thursday, and go back to reaching for the mouse.&lt;/p&gt;

&lt;p&gt;A shorter list works better, as long as it's in the right order. A small set of shortcuts covers most of a normal day: finding a file, finding a command, changing many lines at once, and moving around the editor. Learn those seven first and let everything else wait until you actually need it.&lt;/p&gt;

&lt;p&gt;Shortcuts are shown as &lt;strong&gt;Windows/Linux / Mac&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start here: the Command Palette
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;Ctrl+Shift+P&lt;/code&gt; / &lt;code&gt;Cmd+Shift+P&lt;/code&gt; opens the Command Palette.&lt;/strong&gt; This is the one to learn before any other, because it makes every other feature findable. Every command in VS Code, including the ones you've never heard of, is listed in this one search box. Press the shortcut, type a few words of what you want ("format", "word wrap", "select interpreter", "reload"), and pick it from the list.&lt;/p&gt;

&lt;p&gt;It also teaches you the rest. Each command in the list shows its keyboard shortcut on the right, if it has one. Every time you use the palette to do something, you're shown the faster way to do it next time. You don't have to memorize shortcuts up front. You can look them up in the moment and let your hands pick them up over time.&lt;/p&gt;

&lt;p&gt;If you only learn one shortcut this week, make it this one. It's also the answer to "how do I do X in VS Code?" for almost any X.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving around: files and symbols
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;Ctrl+P&lt;/code&gt; / &lt;code&gt;Cmd+P&lt;/code&gt; is Quick Open.&lt;/strong&gt; Type part of a filename and jump straight to it, with no clicking through folders in the Explorer. Fuzzy matching means &lt;code&gt;usrmod&lt;/code&gt; will find &lt;code&gt;user_model.py&lt;/code&gt;. This single habit does more to stop you from hunting through the sidebar than any other.&lt;/p&gt;

&lt;p&gt;Quick Open has a few extra tricks once you're in it. Type &lt;code&gt;:&lt;/code&gt; followed by a number to jump to that line in the current file. Type &lt;code&gt;@&lt;/code&gt; to jump to a function or class in the current file. Type &lt;code&gt;&amp;gt;&lt;/code&gt; and it turns into the Command Palette, which is why the two feel like one tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. &lt;code&gt;Ctrl+`&lt;/code&gt; / &lt;code&gt;Cmd+`&lt;/code&gt; toggles the integrated terminal.&lt;/strong&gt; It opens the terminal at the bottom of the editor, already in your project folder, and the same shortcut hides it again. You'll use this far more often than you expect, and it keeps you out of a separate terminal window.&lt;/p&gt;

&lt;h2&gt;
  
  
  Editing: change more with less typing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;4. &lt;code&gt;Ctrl+D&lt;/code&gt; / &lt;code&gt;Cmd+D&lt;/code&gt; selects the next occurrence.&lt;/strong&gt; Put the cursor on a word and press it once to select the word. Press it again and VS Code selects the next match and gives you a second cursor there. Keep pressing to add more, then type once to edit all of them together. It's the quickest way to rename a variable inside a short block, and a good first taste of multi-cursor editing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. &lt;code&gt;Alt+Up&lt;/code&gt; / &lt;code&gt;Alt+Down&lt;/code&gt; (&lt;code&gt;Option+Up&lt;/code&gt; / &lt;code&gt;Option+Down&lt;/code&gt; on Mac) moves the current line.&lt;/strong&gt; No cutting and pasting. The line moves up or down, and if you've selected several lines, they move together. Reordering code is a constant small chore, and this makes it one keypress.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. &lt;code&gt;F2&lt;/code&gt; renames a symbol.&lt;/strong&gt; Put the cursor on a variable, function, or class name and press &lt;code&gt;F2&lt;/code&gt;. VS Code renames it everywhere it's used, across files if needed, and understands the code well enough not to touch other things that happen to share the text. For renaming real code, this is safer than Find and Replace, which will happily change matching words in comments and strings too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Search: find things across the whole project
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;7. &lt;code&gt;Ctrl+Shift+F&lt;/code&gt; / &lt;code&gt;Cmd+Shift+F&lt;/code&gt; searches the whole project.&lt;/strong&gt; Plain &lt;code&gt;Ctrl+F&lt;/code&gt; only searches the file you're in. This searches every file, shows the matches grouped by file, and lets you click straight to any of them. When you can't remember where something lives, this is how you find it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to actually learn them
&lt;/h2&gt;

&lt;p&gt;Don't try all seven on day one. Learn one at a time, in the order above, and give each a few days before adding the next.&lt;/p&gt;

&lt;p&gt;Pick the one you're about to use and look at it right before you do the task. If you notice yourself clicking through the Explorer to find a file, stop and try &lt;code&gt;Ctrl+P&lt;/code&gt; instead. If you notice yourself copy-pasting a line to move it, try &lt;code&gt;Alt+Up&lt;/code&gt;. The shortcut sticks because you use it at the moment you'd have done it the slow way.&lt;/p&gt;

&lt;p&gt;When you forget one, open the Command Palette and search for it. Since it shows you the shortcut next to the command, forgetting costs you a few seconds and no more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two mistakes worth knowing about ahead of time
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Memorizing shortcuts you don't have a use for yet.&lt;/strong&gt; A shortcut you never need doesn't stick, and learning twenty of them at once is how people end up with a list of shortcuts they can't recall. Add a new one only when you notice yourself doing something the slow way, over and over.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assuming a shortcut is broken when it's just taken.&lt;/strong&gt; Some shortcuts don't work because another program, or your operating system, has already claimed that key combination. If a shortcut does nothing, open the Command Palette, run &lt;code&gt;Preferences: Open Keyboard Shortcuts&lt;/code&gt;, and search for the command. It shows what key it's bound to and lets you change it.&lt;/p&gt;

&lt;h2&gt;
  
  
  A habit that makes the rest easy
&lt;/h2&gt;

&lt;p&gt;Before you reach for the mouse to do something in VS Code, ask yourself whether the Command Palette could do it. Nine times out of ten it can, and the tenth time you'll find out something new about the editor. Over a few weeks that one habit teaches you more than any cheat sheet, because you're always learning the shortcut for the thing you're actually doing.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you'd like more posts like this sent straight to your inbox, &lt;a href="https://buttondown.com/SystemCraftPress?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=vscode-shortcuts-to-learn-first" rel="noopener noreferrer"&gt;subscribe to the newsletter&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Prefer to dig in yourself? The &lt;a href="https://github.com/SystemCraftPress/vscode-essentials?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=vscode-shortcuts-to-learn-first" rel="noopener noreferrer"&gt;VS Code Essentials repo&lt;/a&gt; on GitHub has more free examples and exercises.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>productivity</category>
      <category>beginners</category>
    </item>
    <item>
      <title>'ModuleNotFoundError' Right After pip install — You Installed It for a Different Python</title>
      <dc:creator>SystemCraftDev</dc:creator>
      <pubDate>Sun, 11 Oct 2026 02:46:10 +0000</pubDate>
      <link>https://dev.to/systemcraftdev/modulenotfounderror-right-after-pip-install-you-installed-it-for-a-different-python-39mp</link>
      <guid>https://dev.to/systemcraftdev/modulenotfounderror-right-after-pip-install-you-installed-it-for-a-different-python-39mp</guid>
      <description>&lt;p&gt;The package is installed and Python says it doesn't exist. Both are true: pip and python can belong to different installations. Here's how to see which one is which and how to make them agree.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Adapted from the &lt;a href="https://systemcraftpress.com/guides/python-essentials/?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=modulenotfounderror-after-pip-install" rel="noopener noreferrer"&gt;Python Essentials Companion Guide&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;You need a library, so you install it, and pip reports success:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;termcolor
Successfully installed termcolor-2.4.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you run your script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python app.py
&lt;span class="go"&gt;Traceback (most recent call last):
&lt;/span&gt;&lt;span class="gp"&gt;  File "app.py", line 1, in &amp;lt;module&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;    import termcolor
ModuleNotFoundError: No module named 'termcolor'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both messages are telling the truth, which is exactly why this is so frustrating. pip installed &lt;code&gt;termcolor&lt;/code&gt;. Python can't find &lt;code&gt;termcolor&lt;/code&gt;. Nothing is broken, and nothing was lost. The package is sitting on your machine, just not where the Python that ran your script is looking.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's actually happening
&lt;/h2&gt;

&lt;p&gt;Most machines have more than one Python on them: the one your operating system ships with, one you installed yourself, one inside a virtual environment, one bundled with an editor or tool. Each of those has its own separate folder of installed packages, called &lt;code&gt;site-packages&lt;/code&gt;. A package installed into one copy is invisible to every other copy.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;pip&lt;/code&gt; command and the &lt;code&gt;python&lt;/code&gt; command are two separate programs that each get found by searching your &lt;code&gt;PATH&lt;/code&gt;. Nothing guarantees they point at the same installation. So &lt;code&gt;pip install termcolor&lt;/code&gt; may have put the package into Python A's &lt;code&gt;site-packages&lt;/code&gt;, while &lt;code&gt;python app.py&lt;/code&gt; started Python B, which has never heard of it. When &lt;code&gt;import termcolor&lt;/code&gt; runs, Python B searches only its own folders, comes up empty, and raises &lt;code&gt;ModuleNotFoundError&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's the whole error: "I searched every place &lt;em&gt;this&lt;/em&gt; interpreter looks, and the module isn't in any of them." It says nothing about whether the package exists elsewhere on your computer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix, step by step
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Find out which Python actually ran your script.&lt;/strong&gt; Add this to the top of the file, or run it in the same terminal:&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="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;import sys; print(sys.executable)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The path it prints is the interpreter that failed to find your package, for example &lt;code&gt;C:\Python312\python.exe&lt;/code&gt; or &lt;code&gt;/usr/bin/python3&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Find out where &lt;code&gt;pip&lt;/code&gt; installs things.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output ends with a path and a Python version, something like &lt;code&gt;pip 24.0 from /home/you/.venv/lib/python3.12/site-packages/pip (python 3.12)&lt;/code&gt;. If that path points to a different Python than the one from step 1, you've found the mismatch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Install through the interpreter itself, so the two can't disagree.&lt;/strong&gt; Instead of running &lt;code&gt;pip&lt;/code&gt; directly, run it as a module of the exact &lt;code&gt;python&lt;/code&gt; you use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;termcolor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows, if you run scripts with the launcher, use &lt;code&gt;py -m pip install termcolor&lt;/code&gt;. Because &lt;code&gt;python -m pip&lt;/code&gt; is pip belonging to &lt;em&gt;that&lt;/em&gt; interpreter, the package lands in the same &lt;code&gt;site-packages&lt;/code&gt; your script will search. Run your script again and the import works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. If you use a virtual environment, activate it before you install or run anything.&lt;/strong&gt; The activated environment puts its own &lt;code&gt;python&lt;/code&gt; and &lt;code&gt;pip&lt;/code&gt; at the front of your &lt;code&gt;PATH&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate      &lt;span class="c"&gt;# macOS / Linux&lt;/span&gt;
.venv&lt;span class="se"&gt;\S&lt;/span&gt;cripts&lt;span class="se"&gt;\a&lt;/span&gt;ctivate         &lt;span class="c"&gt;# Windows&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can confirm it worked: &lt;code&gt;sys.executable&lt;/code&gt; from step 1 should now point inside your &lt;code&gt;.venv&lt;/code&gt; folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two mistakes worth knowing about ahead of time
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Reinstalling the package over and over.&lt;/strong&gt; Running &lt;code&gt;pip install&lt;/code&gt; again feels like it should help, and it only reports "Requirement already satisfied", because pip is checking the installation it belongs to, which already has the package. The repeated message is a sign the install is fine and the interpreter is the problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using the wrong name for the import.&lt;/strong&gt; Sometimes the package you install and the module you import have different names, and then you get this same error with a perfectly matching interpreter. &lt;code&gt;pip install python-dotenv&lt;/code&gt; gives you &lt;code&gt;import dotenv&lt;/code&gt;, not &lt;code&gt;import python_dotenv&lt;/code&gt;. Other common pairs are &lt;code&gt;Pillow&lt;/code&gt; (&lt;code&gt;import PIL&lt;/code&gt;), &lt;code&gt;scikit-learn&lt;/code&gt; (&lt;code&gt;import sklearn&lt;/code&gt;), and &lt;code&gt;opencv-python&lt;/code&gt; (&lt;code&gt;import cv2&lt;/code&gt;). If &lt;code&gt;python -m pip show &amp;lt;package&amp;gt;&lt;/code&gt; finds it but the import still fails, check the package's documentation for the real import name.&lt;/p&gt;

&lt;p&gt;Editors add one more layer to this. If the script runs fine in your terminal but your editor shows the same error, the editor is using a different interpreter than your terminal. That case has its own explanation in &lt;a href="https://systemcraftpress.com/blog/vscode-wrong-python-interpreter/?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=modulenotfounderror-after-pip-install" rel="noopener noreferrer"&gt;VS Code Is Running the Wrong Python&lt;/a&gt;.*).&lt;/p&gt;

&lt;h2&gt;
  
  
  A habit that prevents the confusion entirely
&lt;/h2&gt;

&lt;p&gt;Get in the habit of typing &lt;code&gt;python -m pip install ...&lt;/code&gt; instead of bare &lt;code&gt;pip install ...&lt;/code&gt;, every time. It costs a few extra keystrokes and removes the entire class of problem, because the pip you run is by construction the one that belongs to the Python you named. Pair it with one virtual environment per project, activated before you start working, and "it's installed but Python can't see it" becomes something you can diagnose in under a minute: &lt;code&gt;sys.executable&lt;/code&gt; tells you who ran, &lt;code&gt;pip --version&lt;/code&gt; tells you who installed, and the two paths either match or they don't.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you'd like more posts like this sent straight to your inbox, &lt;a href="https://buttondown.com/SystemCraftPress?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=modulenotfounderror-after-pip-install" rel="noopener noreferrer"&gt;subscribe to the newsletter&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Prefer to dig in yourself? The &lt;a href="https://github.com/SystemCraftPress/python-essentials?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=modulenotfounderror-after-pip-install" rel="noopener noreferrer"&gt;Python Essentials repo&lt;/a&gt; on GitHub has more free examples and exercises.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>beginners</category>
      <category>debugging</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>'Bad Interpreter: No Such File or Directory' — The Script Exists, the Interpreter Doesn't</title>
      <dc:creator>SystemCraftDev</dc:creator>
      <pubDate>Mon, 05 Oct 2026 03:35:43 +0000</pubDate>
      <link>https://dev.to/systemcraftdev/bad-interpreter-no-such-file-or-directory-the-script-exists-the-interpreter-doesnt-2o5</link>
      <guid>https://dev.to/systemcraftdev/bad-interpreter-no-such-file-or-directory-the-script-exists-the-interpreter-doesnt-2o5</guid>
      <description>&lt;p&gt;The file is right there, so why does the shell say it doesn't exist? The error is about the interpreter named on the first line, and usually a hidden carriage return is the culprit. Here's how to see it and fix it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Adapted from the &lt;a href="https://systemcraftpress.com/guides/command-line-essentials/?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=bad-interpreter-no-such-file-or-directory" rel="noopener noreferrer"&gt;Command Line Essentials Companion Guide&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;You write a small script, make it executable, and run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; deploy.sh
&lt;span class="go"&gt;-rwxr-xr-x 1 you you 62 Oct  4 09:12 deploy.sh

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;./deploy.sh
&lt;span class="go"&gt;bash: ./deploy.sh: /bin/bash^M: bad interpreter: No such file or directory
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file is sitting right there, &lt;code&gt;ls&lt;/code&gt; just showed it, and the permissions are fine. Yet the shell insists something doesn't exist. This is one of the more disorienting errors in the shell, because the message sounds like it's about your script and it isn't. It's about a different file entirely: the interpreter named on the script's first line. And if you look closely at the message, there's a clue sitting in the path: &lt;code&gt;/bin/bash^M&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's actually happening
&lt;/h2&gt;

&lt;p&gt;When you run &lt;code&gt;./deploy.sh&lt;/code&gt;, the shell doesn't execute the text file itself. The operating system looks at the first line, the shebang (&lt;code&gt;#!/bin/bash&lt;/code&gt;), takes the path after &lt;code&gt;#!&lt;/code&gt; as the program that should run the script, and launches that program with your script as its argument. If that interpreter path doesn't point to a real file, the launch fails with "No such file or directory". The "file" it can't find is the interpreter, not your script.&lt;/p&gt;

&lt;p&gt;So why would &lt;code&gt;/bin/bash&lt;/code&gt; not exist? Because the path isn't &lt;code&gt;/bin/bash&lt;/code&gt;. Look at the &lt;code&gt;^M&lt;/code&gt; at the end. That's how terminals display a carriage return character (&lt;code&gt;\r&lt;/code&gt;), the extra invisible character Windows puts before every newline. A file saved with Windows-style (CRLF) line endings has its first line stored as &lt;code&gt;#!/bin/bash\r\n&lt;/code&gt;. Linux and macOS treat only &lt;code&gt;\n&lt;/code&gt; as the line break, so the &lt;code&gt;\r&lt;/code&gt; stays attached to the end of the path. The system goes looking for a program literally named &lt;code&gt;bash&lt;/code&gt; followed by a carriage return, finds nothing, and reports the mismatch.&lt;/p&gt;

&lt;p&gt;Nothing is wrong with your commands. The file just arrived with the wrong kind of line endings, usually because it was written in a Windows editor, copied from a Windows machine, or checked out of Git on Windows with automatic line-ending conversion turned on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix, step by step
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Confirm it's line endings.&lt;/strong&gt; The &lt;code&gt;file&lt;/code&gt; command reports them directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ file deploy.sh
deploy.sh: Bourne-Again shell script, ASCII text executable, with CRLF line terminators
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"With CRLF line terminators" is the diagnosis. If you want to see the characters themselves, &lt;code&gt;cat -A deploy.sh&lt;/code&gt; shows each carriage return as &lt;code&gt;^M&lt;/code&gt; and each line end as &lt;code&gt;$&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Strip the carriage returns.&lt;/strong&gt; Any of these works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dos2unix deploy.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/\r$//'&lt;/span&gt; deploy.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;dos2unix&lt;/code&gt; is the cleanest if it's installed (&lt;code&gt;sudo apt install dos2unix&lt;/code&gt; on Debian and Ubuntu). The &lt;code&gt;sed&lt;/code&gt; command needs nothing extra: it deletes a trailing &lt;code&gt;\r&lt;/code&gt; from every line, in place.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Run it again.&lt;/strong&gt; Nothing else about the script needs to change, and the executable bit survives the edit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./deploy.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Stop it from coming back.&lt;/strong&gt; Fixing the file once doesn't help if your editor or Git reintroduces the problem next time. In VS Code, click &lt;code&gt;CRLF&lt;/code&gt; in the bottom-right status bar and switch it to &lt;code&gt;LF&lt;/code&gt; for shell scripts. If the file lives in a Git repository, a &lt;code&gt;.gitattributes&lt;/code&gt; line makes the setting stick for everyone who clones it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*.sh text eol=lf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Two mistakes worth knowing about ahead of time
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Retyping the shebang line and hoping for the best.&lt;/strong&gt; Deleting and retyping &lt;code&gt;#!/bin/bash&lt;/code&gt; in the same editor will often produce the same CRLF ending again, because the editor is what's adding it. Fix the setting, not just the line.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assuming &lt;code&gt;^M&lt;/code&gt; errors only affect the first line.&lt;/strong&gt; Every line in the file has the carriage return, which is why scripts without a shebang fail in a different, stranger way: running them with &lt;code&gt;bash deploy.sh&lt;/code&gt; can produce errors like &lt;code&gt;$'\r': command not found&lt;/code&gt; partway through, because each line's trailing &lt;code&gt;\r&lt;/code&gt; is treated as part of the command. Different message, same root cause. If you see &lt;code&gt;\r&lt;/code&gt; or &lt;code&gt;^M&lt;/code&gt; anywhere in shell error output, check line endings first.&lt;/p&gt;

&lt;p&gt;The same error message also appears when the interpreter path is genuinely wrong, for example &lt;code&gt;#!/usr/bin/python&lt;/code&gt; on a system that only has &lt;code&gt;python3&lt;/code&gt;. If &lt;code&gt;file&lt;/code&gt; reports no CRLF terminators, check that the path on the first line points at something that exists (&lt;code&gt;ls /usr/bin/python&lt;/code&gt;), or use &lt;code&gt;#!/usr/bin/env python3&lt;/code&gt; so the system finds it for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  A habit that prevents the confusion entirely
&lt;/h2&gt;

&lt;p&gt;When "No such file or directory" appears for a file you can plainly see, ask which file the error is actually about. If you're running a script, the answer might be the interpreter on the first line rather than the script itself. Run &lt;code&gt;file&lt;/code&gt; on the script and read the first line with &lt;code&gt;head -1 deploy.sh | cat -A&lt;/code&gt;. Two quick commands will tell you whether you have a wrong path or an invisible character, and either way the error stops being mysterious.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you'd like more posts like this sent straight to your inbox, &lt;a href="https://buttondown.com/SystemCraftPress?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=bad-interpreter-no-such-file-or-directory" rel="noopener noreferrer"&gt;subscribe to the newsletter&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Prefer to dig in yourself? The &lt;a href="https://github.com/SystemCraftPress/command-line-essentials?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=bad-interpreter-no-such-file-or-directory" rel="noopener noreferrer"&gt;Command Line Essentials repo&lt;/a&gt; on GitHub has more free examples and exercises.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>bash</category>
      <category>cli</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Why VS Code's Debugger Skips Your Breakpoint — It's Not Broken, It's Not Watching That Line</title>
      <dc:creator>SystemCraftDev</dc:creator>
      <pubDate>Wed, 30 Sep 2026 03:23:23 +0000</pubDate>
      <link>https://dev.to/systemcraftdev/why-vs-codes-debugger-skips-your-breakpoint-its-not-broken-its-not-watching-that-line-1m9n</link>
      <guid>https://dev.to/systemcraftdev/why-vs-codes-debugger-skips-your-breakpoint-its-not-broken-its-not-watching-that-line-1m9n</guid>
      <description>&lt;p&gt;A hollow breakpoint dot isn't VS Code ignoring you — it's telling you the debugger couldn't connect that line to any code it's actually running. Here's what that means and how to fix it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Adapted from the &lt;a href="https://systemcraftpress.com/guides/vscode-essentials/?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=vscode-unverified-breakpoint" rel="noopener noreferrer"&gt;VS Code Essentials Companion Guide&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;You set a breakpoint, click the line, and the usual solid red dot shows up. You start debugging, and the moment execution should hit that line, nothing happens — the code just keeps running past it. Looking back at the gutter, the dot has changed: it's now hollow, a grey circle with a hole in the middle instead of a solid one. Nothing crashed, no error appeared, the debugger just quietly decided not to stop there.&lt;/p&gt;

&lt;p&gt;That hollow dot is the whole story, and it's more specific than it looks. VS Code calls this an "unverified" breakpoint, and the name is accurate: it isn't marking the line as wrong, or your code as broken. It's telling you that the debugger could not connect this exact line in your editor to any line in the code it's actually executing right now. A verified breakpoint (solid red) means the debug adapter found a matching, runnable line and is watching it. An unverified one means it looked and found nothing to bind to — so there's nothing for it to pause on, no matter how many times you run past it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's actually happening
&lt;/h2&gt;

&lt;p&gt;When you start a debug session, VS Code doesn't debug the file you're looking at directly — it hands a running process to a debug adapter (one per language/runtime), and that adapter reports back which lines in which files actually correspond to real, loaded, executable code. Your breakpoint only turns solid once the adapter confirms that mapping exists. Until then, or if it never can, the dot stays hollow.&lt;/p&gt;

&lt;p&gt;The most common reason that mapping fails is a mismatch between the file you're editing and the file that's actually running. For compiled or transpiled languages — TypeScript compiling to JavaScript, or any bundler-based JS project — the runtime executes generated output, not your source file. A source map is what lets the debugger translate "line 42 of your &lt;code&gt;.ts&lt;/code&gt; file" into "line 187 of the bundled &lt;code&gt;.js&lt;/code&gt; file the browser or Node process is actually running." If that source map is missing, stale, or points at the wrong output location, the debugger has no way to make that translation — so a breakpoint on your source line has nothing on the runtime side to bind to, and it stays unverified.&lt;/p&gt;

&lt;p&gt;The second common cause is simpler: you're debugging a different copy of the code than the one you're editing. A &lt;code&gt;dist/&lt;/code&gt; or &lt;code&gt;build/&lt;/code&gt; folder holding yesterday's compiled output, a Docker container running a stale image, or a &lt;code&gt;launch.json&lt;/code&gt; &lt;code&gt;outFiles&lt;/code&gt; setting pointing somewhere that no longer matches your source layout — all of these mean the process VS Code attached to isn't running the code in front of you, even though the file names might look identical.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix, step by step
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Confirm it's actually unverified, not just unhit.&lt;/strong&gt; Hover over the hollow dot — VS Code shows a tooltip explaining why, and for source-map issues it's usually explicit about not being able to find the generated code. That tooltip is worth reading before changing anything; it tells you which of the causes below you're actually dealing with.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. For compiled/transpiled code, check that source maps are being generated and referenced correctly.&lt;/strong&gt; Confirm your build config actually emits &lt;code&gt;.map&lt;/code&gt; files (&lt;code&gt;"sourceMap": true&lt;/code&gt; in &lt;code&gt;tsconfig.json&lt;/code&gt;, or your bundler's equivalent flag), and that your &lt;code&gt;launch.json&lt;/code&gt; has an &lt;code&gt;outFiles&lt;/code&gt; entry pointing at where the compiled output actually lands — a mismatch here is the single most common cause of this exact symptom.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Confirm you're running the code you think you're running.&lt;/strong&gt; Stop the debug session, delete or rebuild your &lt;code&gt;dist&lt;/code&gt;/&lt;code&gt;build&lt;/code&gt; output, and restart. If the breakpoint verifies immediately after a clean rebuild, the previous run was executing stale compiled output, not your current source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. If you're attaching to an already-running process&lt;/strong&gt; (a container, a remote server, a long-running dev server you didn't start from VS Code), double-check that the path mapping between your local source and the remote source matches exactly — a &lt;code&gt;sourceFileMap&lt;/code&gt; or &lt;code&gt;remoteRoot&lt;/code&gt;/&lt;code&gt;localRoot&lt;/code&gt; setting that's slightly off will produce exactly this symptom, because the debugger genuinely can't find your file on the other end.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two mistakes worth knowing about ahead of time
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Assuming a hollow breakpoint means the line is unreachable in your program's logic.&lt;/strong&gt; It doesn't say anything about your control flow — it says the debugger can't map the line to running code at all. A perfectly reachable, frequently executed line can show as unverified for purely mechanical reasons (a stale build), while a genuinely dead line would show as verified and just never get hit — a different, quieter problem with a different fix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Restarting the debug session repeatedly without changing anything.&lt;/strong&gt; If the mapping is broken, restarting the exact same broken setup produces the exact same hollow dot every time. The fix is almost always in the build output or the &lt;code&gt;launch.json&lt;/code&gt; configuration, not in the act of starting a new session — check those before assuming it's a fluke.&lt;/p&gt;

&lt;h2&gt;
  
  
  A habit that prevents the confusion entirely
&lt;/h2&gt;

&lt;p&gt;Any time you're debugging code that goes through a build step — TypeScript, a bundler, a transpiler of any kind — treat source maps as part of your build's correctness, not an afterthought. Confirm they're being generated on every build, not just the first one, and that your &lt;code&gt;launch.json&lt;/code&gt; points at the right output directory before you start hunting for why a breakpoint won't stop. A hollow dot isn't VS Code failing to notice your breakpoint — it's VS Code being honest that, as far as the running process is concerned, that line doesn't currently exist.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you'd like more posts like this sent straight to your inbox, &lt;a href="https://buttondown.com/SystemCraftPress?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=vscode-unverified-breakpoint" rel="noopener noreferrer"&gt;subscribe to the newsletter&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Prefer to dig in yourself? The &lt;a href="https://github.com/SystemCraftPress/vscode-essentials?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=vscode-unverified-breakpoint" rel="noopener noreferrer"&gt;VS Code Essentials repo&lt;/a&gt; on GitHub has more free examples and exercises.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>python</category>
      <category>productivity</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Why Your JOIN Doubled Your Totals — There's No Error, Just the Wrong Number</title>
      <dc:creator>SystemCraftDev</dc:creator>
      <pubDate>Sat, 26 Sep 2026 03:27:04 +0000</pubDate>
      <link>https://dev.to/systemcraftdev/why-your-join-doubled-your-totals-theres-no-error-just-the-wrong-number-5a3i</link>
      <guid>https://dev.to/systemcraftdev/why-your-join-doubled-your-totals-theres-no-error-just-the-wrong-number-5a3i</guid>
      <description>&lt;p&gt;SQL doesn't warn you when a JOIN multiplies your rows — it just quietly hands back more of them than you expected, and every SUM() and COUNT() downstream inherits the mistake. Here's the exact mechanism, and how to catch it before it ships.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Adapted from the &lt;a href="https://systemcraftpress.com/guides/sql-essentials/?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=sql-join-duplicate-rows" rel="noopener noreferrer"&gt;SQL Essentials Companion Guide&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;You write a query to total up each customer's orders, and it runs fine — no error, no red text, just a result set:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&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;total_spent&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The totals come back too high. Not obviously broken — just off, in a way that's easy to miss until someone downstream notices the numbers don't match the invoice system. There's no error here to read, which is exactly what makes this one harder than a syntax mistake: the query is completely valid SQL, and it's doing precisely what you told it to. It's just not doing what you &lt;em&gt;meant&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's actually happening
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;JOIN&lt;/code&gt; doesn't attach related rows to each other one-to-one — it produces every combination of rows that satisfies the join condition. If a customer has three orders, that customer's row from &lt;code&gt;customers&lt;/code&gt; gets matched against all three rows from &lt;code&gt;orders&lt;/code&gt;, and the join produces three separate rows in the result — one per order, each with the &lt;em&gt;same&lt;/em&gt; customer name and id copied across all of them.&lt;/p&gt;

&lt;p&gt;That's correct and often exactly what you want. The trouble starts when there's another join, or another table, contributing rows on top of that. Say each order also has multiple line items, and the query joins in a &lt;code&gt;line_items&lt;/code&gt; table to look up something about the order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&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;total_spent&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;line_items&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;line_items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now every order row gets duplicated once for &lt;em&gt;each&lt;/em&gt; line item on that order — an order with four line items appears four times in the joined result, each copy carrying the same &lt;code&gt;orders.amount&lt;/code&gt;. &lt;code&gt;SUM(orders.amount)&lt;/code&gt; then adds that same amount in four times over, once per duplicate. Nothing in the query is malformed; the join is doing exactly what a join does. The aggregate is just summing over a result set that's bigger than the one you were picturing — this is usually called a "fan-out," and it's the most common way a JOIN silently corrupts a total.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix, step by step
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Confirm the row count, not just the total.&lt;/strong&gt; Before trusting any &lt;code&gt;SUM()&lt;/code&gt; or &lt;code&gt;COUNT()&lt;/code&gt; after a join, run the join alone without the aggregate and check how many rows come back for a customer or order you can verify by hand. If a customer with 3 orders and no line-item join returns 3 rows, but adding the &lt;code&gt;line_items&lt;/code&gt; join bumps that to 11, you've found your fan-out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Identify which joined table is one-to-many relative to what you're aggregating.&lt;/strong&gt; In this example, &lt;code&gt;orders&lt;/code&gt; is one-to-many with &lt;code&gt;line_items&lt;/code&gt; — the join is multiplying orders, not customers. The fix has to happen at that join, not by changing the &lt;code&gt;SUM()&lt;/code&gt; itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Aggregate the one-to-many side separately, before joining it up&lt;/strong&gt;, so the multiplication never reaches your main total:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_totals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&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;total_spent&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&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;order_totals&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;order_totals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you genuinely need data from &lt;code&gt;line_items&lt;/code&gt; elsewhere in the query, aggregate it in its own subquery (one row per order) before joining that summary in — never join the raw, un-aggregated many-side table into a query that's also summing a different column.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. When you can't restructure the joins, use &lt;code&gt;DISTINCT&lt;/code&gt; or a pre-aggregated CTE as a stopgap&lt;/strong&gt; — but treat it as a stopgap. &lt;code&gt;SUM(DISTINCT orders.amount)&lt;/code&gt; will silently drop legitimate duplicate amounts (two different orders that happen to both be $50), so it trades one silent bug for another. The subquery approach in step 3 is the actual fix; &lt;code&gt;DISTINCT&lt;/code&gt; is what you reach for only when you're debugging in a hurry and plan to come back.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two mistakes worth knowing about ahead of time
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Trusting a query because it ran without an error.&lt;/strong&gt; Fan-out joins are syntactically perfect SQL — there's nothing for the database to reject. The only way to catch this class of bug is checking row counts and spot-verifying a total by hand against a case you already know the right answer for, not waiting for the database to complain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adding more joins to a query that already has an aggregate, without re-checking the totals.&lt;/strong&gt; Every additional &lt;code&gt;JOIN&lt;/code&gt; is a potential new fan-out, even if the query worked correctly before you added it. A query that correctly summed order totals yesterday can start silently inflating them today, the moment someone adds one more join to pull in an unrelated column — the aggregate function didn't change, but the row set underneath it did.&lt;/p&gt;

&lt;h2&gt;
  
  
  A habit that prevents the confusion entirely
&lt;/h2&gt;

&lt;p&gt;Any time a query joins more than two tables and also aggregates with &lt;code&gt;SUM()&lt;/code&gt;, &lt;code&gt;COUNT()&lt;/code&gt;, or &lt;code&gt;AVG()&lt;/code&gt;, ask which table is on the "many" side of each join relative to what you're totaling — and aggregate that side down to one row per key &lt;em&gt;before&lt;/em&gt; it reaches the rest of the query, not after. Joins and aggregates are both doing exactly what they're defined to do; the bug is always in which rows exist by the time the aggregate function runs over them, not in the aggregate function itself.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you'd like more posts like this sent straight to your inbox, &lt;a href="https://buttondown.com/SystemCraftPress?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=sql-join-duplicate-rows" rel="noopener noreferrer"&gt;subscribe to the newsletter&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Prefer to dig in yourself? The &lt;a href="https://github.com/SystemCraftPress/sql-essentials?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=sql-join-duplicate-rows" rel="noopener noreferrer"&gt;SQL Essentials repo&lt;/a&gt; on GitHub has more free examples and exercises.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>sql</category>
      <category>database</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Fix 'Updates Were Rejected' (Without Force-Pushing and Losing Someone's Work)</title>
      <dc:creator>SystemCraftDev</dc:creator>
      <pubDate>Wed, 23 Sep 2026 02:18:29 +0000</pubDate>
      <link>https://dev.to/systemcraftdev/how-to-fix-updates-were-rejected-without-force-pushing-and-losing-someones-work-p0b</link>
      <guid>https://dev.to/systemcraftdev/how-to-fix-updates-were-rejected-without-force-pushing-and-losing-someones-work-p0b</guid>
      <description>&lt;p&gt;This message isn't Git blocking you arbitrarily — it's telling you the remote has commits your local branch doesn't have yet. Here's what to actually do instead of reaching for --force.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Adapted from the &lt;a href="https://systemcraftpress.com/guides/git-github/?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=git-updates-were-rejected" rel="noopener noreferrer"&gt;Git &amp;amp; GitHub Companion Guide&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;You finish up some work, run &lt;code&gt;git push&lt;/code&gt;, and instead of the usual quiet success, you get this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git push
&lt;span class="go"&gt;To github.com:you/project.git
&lt;/span&gt;&lt;span class="gp"&gt; ! [rejected]        main -&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;main &lt;span class="o"&gt;(&lt;/span&gt;fetch first&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;error: failed to push some refs to 'github.com:you/project.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing about your code changed, nothing looks broken locally, and the message reads like Git is just refusing to cooperate. It isn't. Git is telling you something specific and true: the remote branch has commits your local branch doesn't have — probably a teammate pushed since you last pulled, or you pushed from another machine — and it won't let your push proceed until you deal with that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's actually happening
&lt;/h2&gt;

&lt;p&gt;A normal &lt;code&gt;git push&lt;/code&gt; is only allowed to move a branch forward — Git calls this a fast-forward. Your local branch has to already contain every commit that's currently on the remote branch, plus whatever new commits you're adding on top. That's the whole rule.&lt;/p&gt;

&lt;p&gt;The rejection means that condition isn't met: &lt;code&gt;origin/main&lt;/code&gt; has at least one commit your local &lt;code&gt;main&lt;/code&gt; has never seen. If Git let the push through anyway, the only way to make the remote branch match what you're sending is to replace its history — which would silently throw away whatever commit the remote has that you don't. Git isn't being cautious for no reason here. This exact check is what stops you from erasing a commit you didn't even know existed, just because you happened to push first.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix, step by step
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Confirm what you're actually missing.&lt;/strong&gt; Fetch first, without touching your working files, then look at what's there:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git fetch
git log HEAD..origin/main &lt;span class="nt"&gt;--oneline&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That second command lists commits that exist on the remote but not in your local branch — exactly what the rejection is protecting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Integrate those commits into your branch.&lt;/strong&gt; Two ways to do this, and the difference matters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git pull
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fetches and merges the remote changes into your branch, creating a merge commit if both sides have new work. Your local commits stay exactly as they were, just combined with the remote ones.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git pull &lt;span class="nt"&gt;--rebase&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fetches, then replays your local commits on top of the remote ones instead of merging. History stays linear — no merge commit — but your commits get new hashes, since they're technically new commits built on a different base.&lt;/p&gt;

&lt;p&gt;Either is fine for most day-to-day work; which one your team prefers is a style choice, not a correctness one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Resolve any conflicts that come up.&lt;/strong&gt; If you and the remote both touched the same lines, Git will stop and ask you to resolve them, the same as any merge — this is a separate, normal step, not a sign that something's gone wrong with the push itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Push again.&lt;/strong&gt; Your branch now contains every commit the remote has, plus yours — a clean fast-forward, and the push goes through without needing anything special.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two mistakes worth knowing about ahead of time
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Reaching for &lt;code&gt;git push --force&lt;/code&gt; to make the rejection go away.&lt;/strong&gt; Force-pushing tells Git "don't check any of that, just make the remote match my local branch exactly" — which means whatever commit the remote had that you didn't have is now gone from the branch, not merged in, not preserved anywhere obvious. On a shared branch, that's someone else's work disappearing without warning. There are legitimate reasons to force-push — cleaning up your own commits on a branch nobody else has pulled yet, for instance — but "the normal push got rejected" isn't one of them. If you ever do need to force-push deliberately, use &lt;code&gt;git push --force-with-lease&lt;/code&gt; instead of plain &lt;code&gt;--force&lt;/code&gt;: it still refuses if the remote has commits you haven't fetched, so you can't blow away work you haven't even looked at by accident.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assuming the hint's "fetch first" means fetching alone fixes it.&lt;/strong&gt; &lt;code&gt;git fetch&lt;/code&gt; only updates your local copy of the remote branch (&lt;code&gt;origin/main&lt;/code&gt;) — it doesn't touch your actual working branch or merge anything in. Run just &lt;code&gt;git fetch&lt;/code&gt; and then &lt;code&gt;git push&lt;/code&gt; again, and you'll get the exact same rejection, because your branch still hasn't changed. You need the integration step — &lt;code&gt;git pull&lt;/code&gt;, or &lt;code&gt;git merge origin/main&lt;/code&gt; / &lt;code&gt;git rebase origin/main&lt;/code&gt; after fetching — not just the fetch.&lt;/p&gt;

&lt;h2&gt;
  
  
  A habit that prevents the confusion entirely
&lt;/h2&gt;

&lt;p&gt;On any branch other people are also pushing to, pull before you push, not just after you're rejected — especially if it's been more than a few minutes since you last synced. Treat "it's been a while" as the signal to check, the same way you'd check for new messages before replying to a thread. The rejection itself is never the actual problem; it's Git catching you a step behind where the branch actually is. Pulling first just means you find that out before you've already written your push command, instead of after.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you'd like more posts like this sent straight to your inbox, &lt;a href="https://buttondown.com/SystemCraftPress?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=git-updates-were-rejected" rel="noopener noreferrer"&gt;subscribe to the newsletter&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Prefer to dig in yourself? The &lt;a href="https://github.com/SystemCraftPress/git-github?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=git-updates-were-rejected" rel="noopener noreferrer"&gt;Git &amp;amp; GitHub repo&lt;/a&gt; on GitHub has more free examples and exercises.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Why console.log Shows 'Promise {&lt;pending&gt;}' Instead of Your Actual Value</title>
      <dc:creator>SystemCraftDev</dc:creator>
      <pubDate>Sat, 19 Sep 2026 02:26:42 +0000</pubDate>
      <link>https://dev.to/systemcraftdev/why-consolelog-shows-promise-instead-of-your-actual-value-1c60</link>
      <guid>https://dev.to/systemcraftdev/why-consolelog-shows-promise-instead-of-your-actual-value-1c60</guid>
      <description>&lt;p&gt;That's not a broken await — it's JavaScript reporting exactly what the promise's state was the instant you logged it. Here's why the value isn't there yet, and how to actually wait for it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Adapted from the &lt;a href="https://systemcraftpress.com/guides/javascript-essentials/?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=promise-pending-console-log" rel="noopener noreferrer"&gt;JavaScript Essentials Companion Guide&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;You write a function to fetch a user, call it, and log the result to check it worked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/users/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&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 javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;Promise&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;pending&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No error, no crash — just that. It looks like the function silently failed, or that &lt;code&gt;console.log&lt;/code&gt; is somehow broken. Neither is true. JavaScript is telling you, accurately, exactly what &lt;code&gt;user&lt;/code&gt; was at the precise moment you logged it: not a user object, but a promise that hadn't settled yet. That's not a placeholder for a bug — it's a snapshot of real, correct state.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's actually happening
&lt;/h2&gt;

&lt;p&gt;An &lt;code&gt;async&lt;/code&gt; function always returns a promise. Always — even if every line inside it looks like it's returning a plain value. Write &lt;code&gt;return someValue&lt;/code&gt; inside an &lt;code&gt;async function&lt;/code&gt;, and the &lt;em&gt;caller&lt;/em&gt; still gets back a promise that will eventually resolve to &lt;code&gt;someValue&lt;/code&gt;, not &lt;code&gt;someValue&lt;/code&gt; itself. That's not a special case you opt into; it's what the &lt;code&gt;async&lt;/code&gt; keyword does to the function's return type, unconditionally.&lt;/p&gt;

&lt;p&gt;Here's the part that actually causes the confusion: &lt;code&gt;getUser(42)&lt;/code&gt; starts running immediately, synchronously, right up until it hits something it has to wait on — here, the &lt;code&gt;fetch&lt;/code&gt; call. At that point it hands control back to whatever called it and steps aside, to be resumed later once the network response actually arrives. Your &lt;code&gt;console.log(user)&lt;/code&gt; on the next line doesn't wait around for that — it runs immediately, the same tick, before the fetch has had any chance to complete. So &lt;code&gt;user&lt;/code&gt; genuinely is a pending promise at that instant. &lt;code&gt;console.log&lt;/code&gt; isn't lying to you or failing to unwrap something; it's printing the actual, current value of the variable, and that value just happens to be "a promise that hasn't resolved yet."&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix, step by step
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Confirm this is the pattern.&lt;/strong&gt; If logging something that came from an &lt;code&gt;async&lt;/code&gt; function (or anything documented as returning a promise, like &lt;code&gt;fetch&lt;/code&gt;) prints &lt;code&gt;Promise {&amp;lt;pending&amp;gt;}&lt;/code&gt; — or sometimes &lt;code&gt;Promise {&amp;lt;fulfilled&amp;gt;: ...}&lt;/code&gt; if it resolved fast enough — you're reading the promise wrapper itself, not the value inside it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Decide where you actually need the resolved value&lt;/strong&gt;, then &lt;code&gt;await&lt;/code&gt; it there, inside another &lt;code&gt;async function&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadProfile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// the actual object, not a wrapper&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;await&lt;/code&gt; pauses execution of &lt;code&gt;loadProfile&lt;/code&gt; at that line — and only that line, not the whole program — until the promise settles, then hands you the resolved value directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. If you're not inside an &lt;code&gt;async&lt;/code&gt; function&lt;/strong&gt; (top-level script code in some environments, a plain callback, an older codebase), use &lt;code&gt;.then()&lt;/code&gt; instead — same waiting, different syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anything that needs the resolved value has to live inside that callback, or inside a function you call from it — code after &lt;code&gt;.then(...)&lt;/code&gt; in the outer scope still runs before the promise resolves, same as the original bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two mistakes worth knowing about ahead of time
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Putting &lt;code&gt;await&lt;/code&gt; inside &lt;code&gt;.forEach()&lt;/code&gt; and assuming it pauses the outer function.&lt;/strong&gt; This one catches people who've otherwise fully internalized &lt;code&gt;await&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;done&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;"done"&lt;/code&gt; logs first, before any user prints. &lt;code&gt;forEach&lt;/code&gt; doesn't know or care that its callback is &lt;code&gt;async&lt;/code&gt; — it fires off each callback and moves on immediately, never looking at (or waiting for) whatever promise that callback returns. The &lt;code&gt;await&lt;/code&gt; inside is real, but it only pauses that one callback invocation, not &lt;code&gt;processAll&lt;/code&gt; itself. A plain &lt;code&gt;for...of&lt;/code&gt; loop with &lt;code&gt;await&lt;/code&gt; inside it does pause the outer function on each iteration; &lt;code&gt;forEach&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt;, and friends never will, regardless of what's inside the callback.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assuming &lt;code&gt;async&lt;/code&gt; on a function means callers automatically get the resolved value.&lt;/strong&gt; &lt;code&gt;async&lt;/code&gt; changes what happens &lt;em&gt;inside&lt;/em&gt; the function and what it returns — it doesn't change how someone calling it behaves. The function itself has no way to force a caller to &lt;code&gt;await&lt;/code&gt; it; forgetting to is a silent, no-error mistake, which is exactly why it's easy to ship.&lt;/p&gt;

&lt;h2&gt;
  
  
  A habit that prevents the confusion entirely
&lt;/h2&gt;

&lt;p&gt;Any time you call something you know returns a promise — your own &lt;code&gt;async&lt;/code&gt; function, &lt;code&gt;fetch&lt;/code&gt;, a library method documented as async — and the very next line uses the result, ask whether that line actually waited for it. If there's no &lt;code&gt;await&lt;/code&gt; and no &lt;code&gt;.then()&lt;/code&gt; wrapping it, it didn't, no matter how the code reads. &lt;code&gt;console.log&lt;/code&gt; printing &lt;code&gt;Promise {&amp;lt;pending&amp;gt;}&lt;/code&gt; isn't a separate bug to debug — it's the same missing-&lt;code&gt;await&lt;/code&gt; mistake, just surfaced at the one place you happened to look at the value instead of wherever it was actually going to be used.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you'd like more posts like this sent straight to your inbox, &lt;a href="https://buttondown.com/SystemCraftPress?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=promise-pending-console-log" rel="noopener noreferrer"&gt;subscribe to the newsletter&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Prefer to dig in yourself? The &lt;a href="https://github.com/SystemCraftPress/javascript-essentials?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=promise-pending-console-log" rel="noopener noreferrer"&gt;JavaScript Essentials repo&lt;/a&gt; on GitHub has more free examples and exercises.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Python's 'UnboundLocalError' — It's Not a Missing Variable, It's Scope Decided in Advance</title>
      <dc:creator>SystemCraftDev</dc:creator>
      <pubDate>Wed, 16 Sep 2026 03:10:44 +0000</pubDate>
      <link>https://dev.to/systemcraftdev/pythons-unboundlocalerror-its-not-a-missing-variable-its-scope-decided-in-advance-3c7f</link>
      <guid>https://dev.to/systemcraftdev/pythons-unboundlocalerror-its-not-a-missing-variable-its-scope-decided-in-advance-3c7f</guid>
      <description>&lt;p&gt;The variable has a value elsewhere in your program — just not in the scope Python already decided this line belongs to. Here's how Python actually assigns scope, and why the fix usually isn't &lt;code&gt;global&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;As featured in &lt;a href="https://pycoders.com/issues/753" rel="noopener noreferrer"&gt;PyCoder's Weekly, Issue #753&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Adapted from the &lt;a href="https://systemcraftpress.com/guides/python-essentials/?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=unboundlocalerror-referenced-before-assignment" rel="noopener noreferrer"&gt;Python Essentials Companion Guide&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;You write a small counter function, and it looks completely reasonable:&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="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;

&lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&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 plaintext"&gt;&lt;code&gt;UnboundLocalError: local variable 'count' referenced before assignment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This one throws people harder than most, because &lt;code&gt;count&lt;/code&gt; obviously exists — it's sitting right there on the line above, initialized to &lt;code&gt;0&lt;/code&gt;. The error sounds like Python has lost track of a variable that plainly exists. It hasn't. Python made a decision about this function before it ever ran a single line of it, and that decision, not a missing value, is what's crashing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's actually happening
&lt;/h2&gt;

&lt;p&gt;Before a Python function runs, Python scans its entire body — top to bottom, in advance — looking for any name that gets assigned to anywhere inside it. If a name is assigned anywhere in the function, Python marks that name as &lt;strong&gt;local to the function&lt;/strong&gt;, for the &lt;em&gt;entire&lt;/em&gt; function body, no matter where the assignment happens to sit or whether it actually runs before other lines that use the name.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;count += 1&lt;/code&gt; is shorthand for &lt;code&gt;count = count + 1&lt;/code&gt; — it's an assignment. So Python sees an assignment to &lt;code&gt;count&lt;/code&gt; inside &lt;code&gt;increment()&lt;/code&gt; and decides, upfront, that &lt;code&gt;count&lt;/code&gt; is a local variable of that function. That decision applies to the whole function, including the read on the right-hand side of &lt;code&gt;count + 1&lt;/code&gt;, which now happens &lt;em&gt;before&lt;/em&gt; Python has assigned anything to that local &lt;code&gt;count&lt;/code&gt;. The &lt;code&gt;count = 0&lt;/code&gt; sitting above the function is a completely different variable, at module scope, that the function's local &lt;code&gt;count&lt;/code&gt; now shadows — and it doesn't get consulted at all.&lt;/p&gt;

&lt;p&gt;That's the whole error, restated precisely: "you're reading a local variable, at a point in the function where it hasn't been given a value yet." It's not that &lt;code&gt;count&lt;/code&gt; doesn't exist anywhere — it's that &lt;em&gt;this&lt;/em&gt; &lt;code&gt;count&lt;/code&gt;, the one this function decided you meant, hasn't been assigned to yet at the line where you're reading it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix, step by step
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Confirm you're hitting this pattern&lt;/strong&gt;: a variable read and assigned in the same function, where the outer/global version was meant to be reused rather than shadowed. If the traceback names a variable you expected to come from outside the function, this is almost always what's happening.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Decide whether the function actually needs to mutate shared state.&lt;/strong&gt; Usually it doesn't, and that's the better fix:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Pass the value in, return the new value, and let the caller decide what to do with it. No scope trickery required, and the function is easier to test in isolation.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;If the function genuinely needs to rebind a module-level name&lt;/strong&gt;, tell Python that on purpose with &lt;code&gt;global&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;
    &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;code&gt;global count&lt;/code&gt; changes Python's upfront scan: it now knows every reference to &lt;code&gt;count&lt;/code&gt; in this function means the module-level one, so there's no local shadow and no ordering problem.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Check for the non-global version of this same trap&lt;/strong&gt;, which is more common in practice: a variable assigned only inside an &lt;code&gt;if&lt;/code&gt; branch, then read after the &lt;code&gt;if&lt;/code&gt; block, on a code path where that branch never ran:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;classify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&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;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;positive&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# UnboundLocalError when n &amp;lt;= 0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This has nothing to do with global scope — &lt;code&gt;label&lt;/code&gt; is only conditionally assigned, and Python doesn't know that until the function actually runs. The fix is to give it a default before the branch, or add an &lt;code&gt;else&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Two mistakes worth knowing about ahead of time
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Adding &lt;code&gt;global&lt;/code&gt; everywhere out of caution, including to functions that only read the variable.&lt;/strong&gt; You only need &lt;code&gt;global&lt;/code&gt; in a function that &lt;em&gt;assigns&lt;/em&gt; to the name somewhere in its body. A function that only reads a module-level variable — never assigns to it — sees it just fine without any declaration at all. Sprinkling &lt;code&gt;global&lt;/code&gt; on every function that merely mentions a shared name is a sign the scope model isn't fully clicked into place yet, and it tends to spread mutable shared state further than the program actually needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assuming this means "the variable isn't defined yet" in a general sense&lt;/strong&gt;, and hunting for an import order problem or a definition placed too late in the file. The variable is usually defined fine at module scope, well before the function runs. The problem isn't timing at the module level — it's that the function has its own, separate local variable of the same name, and that one hasn't been assigned yet &lt;em&gt;inside this call&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A habit that prevents the confusion entirely
&lt;/h2&gt;

&lt;p&gt;When a function needs to change something that lives outside it, default to passing values in and returning values out, rather than reaching into module-level state with &lt;code&gt;global&lt;/code&gt;. It sidesteps this error category entirely, because there's no local/global name collision to trip over in the first place. Reserve &lt;code&gt;global&lt;/code&gt; for the cases where shared mutable state is genuinely the right design — a counter tied to a long-running process, a cache, a small script where the overhead of threading values through every call isn't worth it — and even then, expect to type &lt;code&gt;global&lt;/code&gt; explicitly the first time you assign to that name in a new function. Python won't infer the intent for you; the upfront scope scan happens the same way every time, regardless of what the variable meant one function ago.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you'd like more posts like this sent straight to your inbox, &lt;a href="https://buttondown.com/SystemCraftPress?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=unboundlocalerror-referenced-before-assignment" rel="noopener noreferrer"&gt;subscribe to the newsletter&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Prefer to dig in yourself? The &lt;a href="https://github.com/SystemCraftPress/python-essentials?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=unboundlocalerror-referenced-before-assignment" rel="noopener noreferrer"&gt;Python Essentials repo&lt;/a&gt; on GitHub has more free examples and exercises.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>beginners</category>
      <category>debugging</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>'Permission Denied' When Running a Script — It's Not Broken, It's Not Executable Yet</title>
      <dc:creator>SystemCraftDev</dc:creator>
      <pubDate>Sun, 13 Sep 2026 02:33:24 +0000</pubDate>
      <link>https://dev.to/systemcraftdev/permission-denied-when-running-a-script-its-not-broken-its-not-executable-yet-2flj</link>
      <guid>https://dev.to/systemcraftdev/permission-denied-when-running-a-script-its-not-broken-its-not-executable-yet-2flj</guid>
      <description>&lt;p&gt;The file is right there. It's not corrupted, and you're not missing a typo. Linux just hasn't been told this file is allowed to run — here's the one-line fix, and why sudo isn't it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Adapted from the &lt;a href="https://systemcraftpress.com/guides/command-line-essentials/?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=permission-denied-running-script" rel="noopener noreferrer"&gt;Command Line Essentials Companion Guide&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;You write a script, save it, and try to run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;./deploy.sh
&lt;span class="go"&gt;-bash: ./deploy.sh: Permission denied
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file is definitely there. &lt;code&gt;cat deploy.sh&lt;/code&gt; prints it out fine. Nothing about the content is wrong. But the shell won't run it, and "Permission denied" sounds like something is badly broken — like the file itself is corrupted or off-limits. It isn't. It's missing one specific, easy-to-forget permission bit, and nothing else.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's actually happening
&lt;/h2&gt;

&lt;p&gt;Every file on a Linux (or macOS) system carries three separate permissions for three separate groups: the owner, the group, and everyone else. Each group can independently have read, write, and execute permission. &lt;code&gt;ls -l&lt;/code&gt; shows this as a ten-character string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; deploy.sh
&lt;span class="go"&gt;-rw-r--r-- 1 you staff 214 Sep 12 09:03 deploy.sh
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read that as three sets of three: &lt;code&gt;rw-&lt;/code&gt; (owner: read, write, no execute), &lt;code&gt;r--&lt;/code&gt; (group: read only), &lt;code&gt;r--&lt;/code&gt; (everyone else: read only). Notice what's missing — there's no &lt;code&gt;x&lt;/code&gt; anywhere in that string. The file is fully readable, which is why &lt;code&gt;cat&lt;/code&gt; works and why the script's contents display just fine in an editor. But readable and executable are two completely different permissions, and creating a new file — via a text editor, &lt;code&gt;touch&lt;/code&gt;, or copying one — doesn't grant execute permission by default. Nothing you did was wrong; that's simply not part of a new file's starting permissions.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Permission denied&lt;/code&gt; here isn't the system protecting something sensitive. It's the system accurately reporting that you haven't yet told it this particular file is meant to be run as a program.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix, step by step
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Confirm this is actually the problem.&lt;/strong&gt; Run &lt;code&gt;ls -l&lt;/code&gt; on the file and look for an &lt;code&gt;x&lt;/code&gt; in the owner's permission set (the first three letters after the leading &lt;code&gt;-&lt;/code&gt;). If it's not there, this is exactly what's happening.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add execute permission:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;chmod&lt;/span&gt; +x deploy.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This adds execute permission for everyone the file's other permissions already allow — for a typical personal script, that's enough. Confirm it worked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;   $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; deploy.sh
&lt;span class="go"&gt;   -rwxr--r-- 1 you staff 214 Sep 12 09:03 deploy.sh
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now there's an &lt;code&gt;x&lt;/code&gt; in the owner's set.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run it again the same way you did before:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   ./deploy.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No other change needed — the script's content was never the issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two mistakes worth knowing about ahead of time
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Reaching for &lt;code&gt;sudo&lt;/code&gt; because "permission" sounds like an authority problem.&lt;/strong&gt; &lt;code&gt;sudo ./deploy.sh&lt;/code&gt; will not fix this — it re-runs the same command as another user, but the file still has no execute bit for anyone, so it fails the same way. &lt;code&gt;sudo&lt;/code&gt; solves "you don't have rights to do this," not "this file hasn't been marked runnable yet." Those are genuinely different problems that happen to share the word "permission."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forgetting the &lt;code&gt;./&lt;/code&gt; and typing just &lt;code&gt;deploy.sh&lt;/code&gt;.&lt;/strong&gt; This produces a different error entirely — usually &lt;code&gt;command not found&lt;/code&gt; — because the shell only searches directories listed in &lt;code&gt;PATH&lt;/code&gt; for bare command names, and your current directory almost never is one of them. &lt;code&gt;./&lt;/code&gt; explicitly means "run the file right here," which is what actually triggers the execute-permission check in the first place. If you're troubleshooting "permission denied" and you're not seeing that exact message, double check you're invoking the file the same way.&lt;/p&gt;

&lt;h2&gt;
  
  
  A habit that prevents the confusion entirely
&lt;/h2&gt;

&lt;p&gt;Any time you write a new script you intend to run directly, &lt;code&gt;chmod +x&lt;/code&gt; it in the same breath as saving it — before you ever try to run it the first time. Treat it as part of "finishing the script," not a fix you reach for after an error. Scripts you download or copy from elsewhere often already have execute permission preserved (or explicitly need it added once, on purpose) — but anything you create fresh with an editor starts without it, every time, and that's expected behavior, not a bug to work around.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you'd like more posts like this sent straight to your inbox, &lt;a href="https://buttondown.com/SystemCraftPress?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=permission-denied-running-script" rel="noopener noreferrer"&gt;subscribe to the newsletter&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Prefer to dig in yourself? The &lt;a href="https://github.com/SystemCraftPress/command-line-essentials?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=permission-denied-running-script" rel="noopener noreferrer"&gt;Command Line Essentials repo&lt;/a&gt; on GitHub has more free examples and exercises.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>bash</category>
      <category>cli</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>VS Code Is Running the Wrong Python — Here's Why the Terminal and IntelliSense Disagree</title>
      <dc:creator>SystemCraftDev</dc:creator>
      <pubDate>Thu, 10 Sep 2026 02:12:12 +0000</pubDate>
      <link>https://dev.to/systemcraftdev/vs-code-is-running-the-wrong-python-heres-why-the-terminal-and-intellisense-disagree-75</link>
      <guid>https://dev.to/systemcraftdev/vs-code-is-running-the-wrong-python-heres-why-the-terminal-and-intellisense-disagree-75</guid>
      <description>&lt;p&gt;Your venv is activated and IntelliSense resolves everything, but the terminal runs a different Python. Here's why VS Code tracks two separate Pythons, and the real fix.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Adapted from the &lt;a href="https://systemcraftpress.com/guides/vscode-essentials/?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=vscode-wrong-python-interpreter" rel="noopener noreferrer"&gt;VS Code Essentials Companion Guide&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;You created a virtual environment, activated it, installed your packages — and VS Code still can't find them. Or the opposite: IntelliSense recognizes everything perfectly, but running the file in the terminal throws &lt;code&gt;ModuleNotFoundError&lt;/code&gt; for a package you know is installed. Either way, it feels like VS Code is lying to you about something as basic as which Python it's using.&lt;/p&gt;

&lt;p&gt;It isn't lying. It's just tracking two separate things that happen to look like one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's actually happening
&lt;/h2&gt;

&lt;p&gt;VS Code has two independent ideas of "your Python," and they don't automatically sync:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The selected interpreter&lt;/strong&gt; is what the Python extension uses for IntelliSense, linting, and Go to Definition. It's set explicitly — via the Status Bar or the &lt;code&gt;Python: Select Interpreter&lt;/code&gt; command — and it sticks until you change it, regardless of what else happens in your project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The terminal's active environment&lt;/strong&gt; is whatever shell state is in effect when a terminal panel opens — usually whichever &lt;code&gt;python&lt;/code&gt; resolves to on your &lt;code&gt;PATH&lt;/code&gt; at that moment, or whatever a &lt;code&gt;venv&lt;/code&gt;'s activation script last set.&lt;/p&gt;

&lt;p&gt;These can drift apart easily. Selecting an interpreter in the Python extension doesn't retroactively fix terminals you already had open. Opening a &lt;em&gt;new&lt;/em&gt; terminal after switching interpreters usually auto-activates the matching environment — but "usually" is doing a lot of work in that sentence, and older terminal panels never get updated at all. The result: IntelliSense and the terminal can each be confidently, independently wrong about which Python is "the" Python, and neither one will tell you the other disagrees.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix, step by step
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check what IntelliSense thinks first.&lt;/strong&gt; Look at the Status Bar at the bottom of the window — it shows the currently selected interpreter (something like &lt;code&gt;Python 3.11.4 ('venv': venv)&lt;/code&gt;). If this is wrong, run:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Python: Select Interpreter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and pick the correct one. This fixes IntelliSense, linting, and autocomplete — but not yet the terminal.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check what the terminal thinks, separately.&lt;/strong&gt; In an actual terminal panel, run:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   which python    &lt;span class="c"&gt;# macOS/Linux&lt;/span&gt;
   where python    &lt;span class="c"&gt;# Windows&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compare that path against the interpreter shown in the Status Bar. If they don't match, that's the whole problem — you have two different Pythons active in two different places.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Open a fresh terminal after fixing the interpreter.&lt;/strong&gt; Close the old terminal panel and open a new one (&lt;code&gt;Ctrl+Shift+`&lt;/code&gt;). A new terminal typically auto-activates the environment matching the currently selected interpreter — an already-open one will not retroactively update.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;If the new terminal still doesn't auto-activate&lt;/strong&gt;, activate the environment manually, the same way you would outside VS Code:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate    &lt;span class="c"&gt;# macOS/Linux&lt;/span&gt;
   venv&lt;span class="se"&gt;\S&lt;/span&gt;cripts&lt;span class="se"&gt;\a&lt;/span&gt;ctivate       &lt;span class="c"&gt;# Windows&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Two mistakes worth knowing about ahead of time
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Assuming "it works when I hover over the import" means the whole file will run correctly.&lt;/strong&gt; IntelliSense resolving a package tells you the &lt;em&gt;selected interpreter&lt;/em&gt; can see it — it says nothing about what a terminal's &lt;code&gt;python file.py&lt;/code&gt; will use. These are genuinely separate checks, and passing one is not evidence for the other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Installing a package a second time because "it's still not found."&lt;/strong&gt; If &lt;code&gt;pip install&lt;/code&gt; in a terminal reports success but the error persists, the terminal and the interpreter installing packages may not be the same Python. Check &lt;code&gt;which python&lt;/code&gt; / &lt;code&gt;where python&lt;/code&gt; before reinstalling anything — you may be about to install into an environment nothing is actually reading from.&lt;/p&gt;

&lt;h2&gt;
  
  
  A habit that prevents the confusion entirely
&lt;/h2&gt;

&lt;p&gt;Whenever a project stops finding a package it should have, check both sides before touching anything: the Status Bar for what IntelliSense is using, and &lt;code&gt;which python&lt;/code&gt; / &lt;code&gt;where python&lt;/code&gt; in the terminal you're about to run code in. If those two don't match, you've found the entire bug already — no reinstalling, no deleting &lt;code&gt;node_modules&lt;/code&gt;-style folders, no guessing. Open a new terminal, confirm it matches, and move on.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you'd like more posts like this sent straight to your inbox, &lt;a href="https://buttondown.com/SystemCraftPress?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=vscode-wrong-python-interpreter" rel="noopener noreferrer"&gt;subscribe to the newsletter&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Prefer to dig in yourself? The &lt;a href="https://github.com/SystemCraftPress/vscode-essentials?utm_source=crosspost&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=vscode-wrong-python-interpreter" rel="noopener noreferrer"&gt;VS Code Essentials repo&lt;/a&gt; on GitHub has more free examples and exercises.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>python</category>
      <category>productivity</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
