<?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>'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>
    <item>
      <title>'Column Must Appear in the GROUP BY Clause' — Why SQL Won't Let You Do That</title>
      <dc:creator>SystemCraftDev</dc:creator>
      <pubDate>Tue, 08 Sep 2026 02:30:00 +0000</pubDate>
      <link>https://dev.to/systemcraftdev/column-must-appear-in-the-group-by-clause-why-sql-wont-let-you-do-that-124a</link>
      <guid>https://dev.to/systemcraftdev/column-must-appear-in-the-group-by-clause-why-sql-wont-let-you-do-that-124a</guid>
      <description>&lt;p&gt;It isn't a syntax error and it isn't arbitrary. SQL is asking a question it genuinely can't answer on its own — and once you see that, the fix is obvious.&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=ql-group-by-clause-error" rel="noopener noreferrer"&gt;SQL Essentials Companion Guide&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;You add one more column to a &lt;code&gt;GROUP BY&lt;/code&gt; query, run it, and get this instead of results:&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="n"&gt;ERROR&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;column&lt;/span&gt; &lt;span class="nv"&gt;"products.name"&lt;/span&gt; &lt;span class="n"&gt;must&lt;/span&gt; &lt;span class="n"&gt;appear&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;the&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;clause&lt;/span&gt;
&lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="k"&gt;aggregate&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The query looked reasonable. Nothing is misspelled. But SQL is refusing to run it at all — not returning wrong data, just flatly declining. That refusal is the whole story: SQL isn't confused about syntax, it's telling you the question you asked doesn't have a single correct answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's actually happening
&lt;/h2&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;category&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;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&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;category&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;GROUP BY category&lt;/code&gt; collapses every row into one row per category. That's the point of grouping — dozens of individual product rows become a handful of category rows. But &lt;code&gt;name&lt;/code&gt; is a per-row value, and each category groups together many different products, each with its own &lt;code&gt;name&lt;/code&gt;. Once those rows are collapsed into one, which product's &lt;code&gt;name&lt;/code&gt; is supposed to show up in the result?&lt;/p&gt;

&lt;p&gt;There's no good answer, and SQL doesn't guess. Every column in the &lt;code&gt;SELECT&lt;/code&gt; list has to be something that still makes sense after the collapse: either a column named in &lt;code&gt;GROUP BY&lt;/code&gt; (one value per group, by definition), or the output of an aggregate function like &lt;code&gt;COUNT()&lt;/code&gt;, &lt;code&gt;SUM()&lt;/code&gt;, or &lt;code&gt;MAX()&lt;/code&gt; (a value computed &lt;em&gt;from&lt;/em&gt; the whole group, so it's well-defined no matter how many rows are in it). &lt;code&gt;name&lt;/code&gt; is neither — it's a leftover from before the grouping happened, and the database won't silently pick one at random on your behalf.&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;Decide what you actually want &lt;code&gt;name&lt;/code&gt; to mean in a grouped result.&lt;/strong&gt; There are two real answers, and the fix depends on which one you meant:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;If you wanted one row per product, not per category&lt;/strong&gt; — you probably don't want to group by category at all, or you need &lt;code&gt;category&lt;/code&gt; and &lt;code&gt;name&lt;/code&gt; to define the group together:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&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;category&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;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&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;category&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 each group is one specific product within one category, so &lt;code&gt;name&lt;/code&gt; has exactly one value per group.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;If you genuinely only want one row per category&lt;/strong&gt;, drop the column that doesn't belong at that level:
&lt;/li&gt;
&lt;/ol&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;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&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;category&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;If you wanted some representative name, not every name&lt;/strong&gt;, wrap it in an aggregate that resolves the ambiguity on purpose — &lt;code&gt;MIN(name)&lt;/code&gt;, &lt;code&gt;MAX(name)&lt;/code&gt;, or, on databases that support it, &lt;code&gt;STRING_AGG(name, ', ')&lt;/code&gt; to list all of them:
&lt;/li&gt;
&lt;/ol&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;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;STRING_AGG&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="s1"&gt;', '&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;product_names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&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;category&lt;/span&gt;&lt;span class="p"&gt;;&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;Reaching for &lt;code&gt;MAX()&lt;/code&gt; or &lt;code&gt;MIN()&lt;/code&gt; just to make the error disappear.&lt;/strong&gt; It works — the query runs — but if you didn't actually mean "the alphabetically last name," you've just replaced a clear error with a quietly wrong result. &lt;code&gt;MAX()&lt;/code&gt;/&lt;code&gt;MIN()&lt;/code&gt; are the right tool when you deliberately want a representative value, not a reflexive fix for an error you haven't read.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assuming this error means the query is broken.&lt;/strong&gt; It's the opposite: this is one of the few SQL errors that catches a real logical mistake &lt;em&gt;before&lt;/em&gt; it produces bad data instead of after. A query that ran without complaint and silently picked one arbitrary &lt;code&gt;name&lt;/code&gt; per group (which is what some databases, like older MySQL configurations, actually did before enforcing this rule) is far more dangerous than one that stops and asks you to clarify.&lt;/p&gt;

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

&lt;p&gt;Before adding any column to a &lt;code&gt;GROUP BY&lt;/code&gt; query's &lt;code&gt;SELECT&lt;/code&gt; list, ask what it's supposed to represent once rows collapse into groups: one value per group (put it in &lt;code&gt;GROUP BY&lt;/code&gt;), a computed summary across the group (wrap it in an aggregate), or something else — in which case it probably means you're grouping by the wrong thing, or trying to answer two different questions in one query. The error isn't the database being pedantic. It's the one moment SQL forces you to answer a question you'd otherwise skip past.&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-group-by-clause-error" 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-group-by-clause-error" 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>Git Says You're in 'Detached HEAD State' — Here's What That Actually Means</title>
      <dc:creator>SystemCraftDev</dc:creator>
      <pubDate>Fri, 04 Sep 2026 03:01:03 +0000</pubDate>
      <link>https://dev.to/systemcraftdev/git-says-youre-in-detached-head-state-heres-what-that-actually-means-310c</link>
      <guid>https://dev.to/systemcraftdev/git-says-youre-in-detached-head-state-heres-what-that-actually-means-310c</guid>
      <description>&lt;p&gt;It reads like an error. It isn't one. Git is just telling you exactly where you are — and getting back to a branch takes one command, once you know why.&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-detached-head-state" rel="noopener noreferrer"&gt;Git &amp;amp; GitHub Companion Guide&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;You check out a specific commit to look at some old code, and Git prints this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Note: switching to 'a3f92c1'.

You are in 'detached HEAD' state...

HEAD is now at a3f92c1 Fix login redirect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing crashed. There's no red text, no &lt;code&gt;error:&lt;/code&gt;, no &lt;code&gt;fatal:&lt;/code&gt;. But "detached HEAD" sounds like something broke, so the instinct is to back out immediately and hope nothing was damaged. Nothing was.&lt;/p&gt;

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

&lt;p&gt;Normally, &lt;code&gt;HEAD&lt;/code&gt; — Git's pointer to "where you currently are" — points at a branch, and that branch points at a commit. When you commit, the branch moves forward and &lt;code&gt;HEAD&lt;/code&gt; moves with it, because &lt;code&gt;HEAD&lt;/code&gt; is really just following the branch.&lt;/p&gt;

&lt;p&gt;Checking out a specific commit directly (instead of a branch name) breaks that chain on purpose. &lt;code&gt;HEAD&lt;/code&gt; now points straight at the commit, with no branch in between — "detached" from any branch. Git isn't warning you about damage. It's telling you, accurately, that if you commit right now, those commits won't belong to any branch — and will be effectively orphaned the moment you check out something else.&lt;/p&gt;

&lt;p&gt;That's the entire risk: not corruption, just the possibility of doing new work in a spot Git won't automatically keep track of.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;If you're just looking around&lt;/strong&gt; — reading old code, checking what a file looked like at that commit — you don't need to do anything. Look, then check out &lt;code&gt;main&lt;/code&gt; (or whatever branch you were on) when you're done:
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;/div&gt;



&lt;p&gt;Nothing you did in detached HEAD state affects your branches at all.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;If you started making changes and want to keep them&lt;/strong&gt;, don't switch branches yet — that's the one action that can strand your work. Instead, turn your current position into a real branch:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; recovery-branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a new branch pointed at exactly where you are, commits and all, and reattaches &lt;code&gt;HEAD&lt;/code&gt; to it. Nothing is lost.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;If you made commits, switched away, and now can't find them&lt;/strong&gt;, they're almost certainly still there — just unreferenced by any branch. &lt;code&gt;git reflog&lt;/code&gt; shows every position &lt;code&gt;HEAD&lt;/code&gt; has recently been at, including ones no branch points to:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;   git reflog
&lt;/span&gt;&lt;span class="gp"&gt;   git checkout - recovery-branch &amp;lt;commit-hash&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&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;Panicking and running something destructive.&lt;/strong&gt; Detached HEAD state is not an error state — it's a completely normal, intentional part of how Git works, used constantly for things like checking out a tag or reviewing history. Running &lt;code&gt;git reset --hard&lt;/code&gt; or anything else "just to be safe" is far more likely to lose work than the detached state itself ever was.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Making real changes without realizing you're still detached.&lt;/strong&gt; This is the only version of this situation that can actually bite you — writing several commits' worth of work while detached, then checking out &lt;code&gt;main&lt;/code&gt; without first branching off, which leaves those commits reachable only through &lt;code&gt;git reflog&lt;/code&gt; (and reflog entries eventually expire). If you're not sure whether you're attached to a branch, &lt;code&gt;git status&lt;/code&gt; tells you immediately — it says &lt;code&gt;HEAD detached at &amp;lt;commit&amp;gt;&lt;/code&gt; right at the top when you're in this state, and names your branch when you're not.&lt;/p&gt;

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

&lt;p&gt;Before doing any real work after a &lt;code&gt;git checkout &amp;lt;commit-hash&amp;gt;&lt;/code&gt;, get in the habit of checking &lt;code&gt;git status&lt;/code&gt; first. If it says detached, branch off with &lt;code&gt;git checkout -b&lt;/code&gt; before writing a single line — that one habit turns "I might have lost my commits" into "I never could have lost my commits" every time.&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-detached-head-state" 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-detached-head-state" 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 Your JavaScript Loop Logs the Same Value Every Time (And How to Fix It)</title>
      <dc:creator>SystemCraftDev</dc:creator>
      <pubDate>Sat, 29 Aug 2026 04:18:47 +0000</pubDate>
      <link>https://dev.to/systemcraftdev/why-your-javascript-loop-logs-the-same-value-every-time-and-how-to-fix-it-he</link>
      <guid>https://dev.to/systemcraftdev/why-your-javascript-loop-logs-the-same-value-every-time-and-how-to-fix-it-he</guid>
      <description>&lt;p&gt;It looks like a timing bug in setTimeout. It isn't. It's var's scope rules doing exactly what they're designed to do — and the fix is one keyword, once you know why.&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=javascript-loop-settimeout-same-value" rel="noopener noreferrer"&gt;JavaScript Essentials Companion Guide&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;You write a loop that should print 0, 1, and 2, one second apart. Instead you get three 3s.&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;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;i&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// 3&lt;/span&gt;
&lt;span class="c1"&gt;// 3&lt;/span&gt;
&lt;span class="c1"&gt;// 3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing crashed. No error. The values just aren't what the loop clearly seems to promise. The first instinct is usually to suspect &lt;code&gt;setTimeout&lt;/code&gt; itself — some kind of timing quirk, a race condition, maybe the callbacks are firing out of order. None of that is what's happening.&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;var&lt;/code&gt; is function-scoped, not block-scoped. That means every single pass through the loop isn't creating a new &lt;code&gt;i&lt;/code&gt; — there's exactly one &lt;code&gt;i&lt;/code&gt; for the entire loop, shared by all three &lt;code&gt;setTimeout&lt;/code&gt; callbacks. By the time any of those callbacks actually runs (a full second later, long after the loop has already finished all three iterations), &lt;code&gt;i&lt;/code&gt; has already reached its final value: &lt;code&gt;3&lt;/code&gt;. All three callbacks look up the same variable, at the same moment, after the loop is long done — so they all see the same thing.&lt;/p&gt;

&lt;p&gt;This isn't a bug in &lt;code&gt;setTimeout&lt;/code&gt;, and it isn't a race condition. The loop runs to completion essentially instantly; the callbacks are what's delayed, and they're all reading from a single shared box that's already empty of the values you wanted by the time they check it.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Recognize the signature&lt;/strong&gt;: a loop paired with a callback or &lt;code&gt;setTimeout&lt;/code&gt;, where every output is identical — and it's always the &lt;em&gt;final&lt;/em&gt; value the loop variable reached, never the first or the middle ones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Change &lt;code&gt;var&lt;/code&gt; to &lt;code&gt;let&lt;/code&gt;&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;   &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;i&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="c1"&gt;// 0&lt;/span&gt;
   &lt;span class="c1"&gt;// 1&lt;/span&gt;
   &lt;span class="c1"&gt;// 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Understand why that one-word change works&lt;/strong&gt;: unlike &lt;code&gt;var&lt;/code&gt;, &lt;code&gt;let&lt;/code&gt; is block-scoped — it creates a &lt;em&gt;new&lt;/em&gt; &lt;code&gt;i&lt;/code&gt;, freshly bound, for every single iteration of the loop. Each &lt;code&gt;setTimeout&lt;/code&gt; callback closes over its own separate &lt;code&gt;i&lt;/code&gt;, not one shared variable, so each one remembers the value it was handed at that specific point in the loop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confirm the fix conceptually, not just empirically&lt;/strong&gt; — if you're not sure why swapping the keyword fixed it, you'll hit the same shape of bug again the next time it shows up somewhere &lt;code&gt;let&lt;/code&gt; isn't the obvious first move (a closure inside a function, for example, not just a loop).&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;Assuming it's a &lt;code&gt;setTimeout&lt;/code&gt; or async timing problem.&lt;/strong&gt; This bug shows up identically with any deferred callback — event listeners, promises, array method callbacks assigned to run later — not just &lt;code&gt;setTimeout&lt;/code&gt;. If you find yourself trying to "fix" it by reordering code or adding delays, that's a sign you're debugging the wrong layer. The loop already finished; nothing about timing changes what value is left behind.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reaching for the old IIFE trick out of habit, without knowing why &lt;code&gt;let&lt;/code&gt; alone is now enough.&lt;/strong&gt; Before &lt;code&gt;let&lt;/code&gt; existed, the standard fix was wrapping the loop body in an immediately-invoked function expression to force a new scope by hand. That still works, but it's solving a problem &lt;code&gt;let&lt;/code&gt; already solves natively — if you're writing new code in 2026 and still reaching for an IIFE here, it's worth understanding that &lt;code&gt;let&lt;/code&gt;'s per-iteration binding was specifically designed to make that pattern unnecessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  A debugging habit that works
&lt;/h2&gt;

&lt;p&gt;Whenever a loop-plus-callback combination prints the same value repeatedly, don't start by investigating the callback's logic — check the loop variable's declaration first. &lt;code&gt;var&lt;/code&gt; shared across every iteration versus &lt;code&gt;let&lt;/code&gt; scoped fresh to each one explains this entire category of bug, and recognizing that signature immediately turns a confusing multi-minute debugging session into a one-word fix.&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=javascript-loop-settimeout-same-value" 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=javascript-loop-settimeout-same-value" 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>
  </channel>
</rss>
