Home·Error messages

Error messages, explained

104 error messages, each with what it means, why it happened and what to do.

The fixes have costs. git reset --hard discards uncommitted work, a force push can destroy a colleague’s commits, and docker system prune deletes unnamed volumes. Where that is the case, the entry says so.

Git29

Almost every git error is a refusal that means "doing that from this state would lose something"; fatal: means git stopped without changing anything, and the hint: lines under the first line usually carry the actual remedy.

fatal: refusing to merge unrelated historiesThe two sides you are merging share no common ancestor at all, so as far as git is concerned they are two unrelated projects. It usually appears when you started locally with git init, made a few commits, then added a remote that already had commits of its own and pulled. --allow-unrelated-histories does merge them, but it welds two unrelated histories together at a single commit and that is hard to unpick later; with only a few local commits it is cleaner to clone the remote fresh and copy your files into it.Your branch and 'origin/main' have divergedYour branch and origin/main each hold commits the other does not, so the line of history has split in two. It happens when you committed locally while someone else pushed, or when you rewrote already-pushed commits with amend or rebase. git pull --rebase replays your commits on top of theirs and keeps history in one line, but it gives your commits new hashes, so if you had already shared them the same work ends up recorded twice; in that case --no-rebase, which leaves a single merge commit, is the safer choice.fatal: Need to specify how to reconcile divergent branches.Since git 2.34, pull refuses to guess whether to merge or rebase when the branches have diverged — it is asking for a setting, not reporting damage. You see it when the branches diverged and pull.rebase has never been configured. pull.ff only is the safest answer because it only pulls when it can fast-forward and otherwise stops without creating anything; pull.rebase true rewrites the hashes of your local commits every time, and false leaves a merge commit.CONFLICT (content): Merge conflict in src/app.tsxBoth sides changed the same lines differently and git cannot decide which is right, so it wrote both versions into the file between <<<<<<<, ======= and >>>>>>> and stopped. It happens when you and someone else edited the same region, or when a rebase carries your commit across an edit of those lines. Open the file, make it what it should be, delete the markers, then git add it and commit. git merge --abort returns you exactly to the state before the merge, which is safe for anything already committed but throws away the conflict resolutions you had done by hand.Please commit your changes or stash them before you merge.Files you have uncommitted edits in are also touched by the commits coming in, and git chose to stop rather than overwrite them. It appears when you pull or merge with a dirty working tree. git stash push -u sets those edits aside so the merge can run, and -u includes untracked files as well; but git stash pop can conflict when you bring them back, and a stash is invisible next to your branches and easy to forget and lose — making a throwaway commit instead is the safer habit.error: Your local changes to the following files would be overwritten by checkout:You are trying to move to another branch, but files whose content differs there still hold edits you have not committed. It appears when you switch or checkout with work half done. git stash push -u, or a throwaway commit, gets you across and lets you come back to it. The git checkout --force and git switch --discard-changes you will find in search results do move you, but they delete those uncommitted edits permanently — nothing records them, not even the reflog, so there is no way back.You are currently rebasing branch 'feature' on '8a3f21c'.A rebase started and stopped partway, and HEAD is parked on a temporary state rather than on your branch. You see this line after a conflict during the rebase, or after an edit or break stop in git rebase -i, when you went off and did something else instead of finishing. Resolve the conflict, git add it and continue with git rebase --continue, or use --skip if you mean to drop that commit. git rebase --abort puts you back exactly where you were before the rebase, so no committed work is lost, but the conflict resolutions you have done so far go with it.You are in 'detached HEAD' state.HEAD points straight at a commit instead of at a branch name, so any commit you make here gets no name attached to it. You end up here by checking out a commit hash or a tag, by working inside a submodule, or because CI checked out one specific commit. To keep what you did, git switch -c name creates a branch right here and loses nothing. If you simply git switch main instead, the commits you made have nothing pointing at them: the reflog can still find them for a while, and then garbage collection removes them.Warning: you are leaving 1 commit behind, not connected to any of your branches:It warns that you committed while in detached HEAD and are now leaving, with no branch pointing at those commits. It happens when you checked out a commit hash, worked, committed, and then went back to a branch. Use the hash printed in the message: git branch rescue 8a3f21c pins them to a name, and that command changes nothing else — it only adds a name. If you already left, git reflog still lists them, but commits nothing points at are removed once garbage collection runs, which by default means roughly thirty days.error: failed to push some refs to 'https://github.com/user/repo.git'The server rejected your push, and this line is only the summary — the real reason sits in the ! [rejected] line just above it. Usually the remote has commits you do not, but a protected branch, a server-side hook or a file-size limit produce the same summary. Read the reason first; for the ordinary case git pull --rebase && git push finishes it. The dangerous move is adding --force without reading, because that can erase a colleague's commits from the server.! [rejected] main -> main (fetch first)The remote branch holds commits your clone has never seen, so pushing now would not be a fast-forward. It happens when a colleague pushed first, or when you last fetched a long time ago. git pull --rebase origin main brings those commits in and replays yours on top, and then the push goes through. Never reach for --force here: the commits you would overwrite are someone else's and are not in your repository at all, so nothing local can bring them back.! [rejected] main -> main (non-fast-forward)Your branch is not a descendant of the remote branch, so pushing as is would drop commits that exist on the server. It happens when you rewrote already-pushed commits with rebase, amend or reset. If the rewrite was deliberate and the branch is yours alone, use git push --force-with-lease: it refuses when the remote moved since your last fetch, so unlike plain --force it cannot silently erase a colleague's push that landed in between. If the rewrite was not deliberate, the answer is pull --rebase, not force.Updates were rejected because the remote contains work that you do not have locally.This is the explanation behind a rejected push: the remote contains work your clone does not have, and pushing now would remove it. It appears when several people share a branch, or when you edited and committed a file in the web interface and never pulled it down. git pull --rebase origin main first, then push, and it goes straight through. Adding --force in defiance of this hint does exactly what the hint exists to prevent: the other person's commits vanish from the server, and if nobody fetched after they pushed, they exist nowhere.error: src refspec main does not match anyThe name you asked git to push does not exist locally as a branch or a tag, and it cannot send what is not there. It happens in a brand-new repository with no commits yet, so main does not exist; or your local branch is master while you typed main; or it is simply a typo. git push -u origin HEAD pushes whatever branch you are actually standing on, under its real name, which clears the mismatched-name case in one step. If there are no commits at all, make one first — an empty repository has nothing to send.fatal: The current branch feature has no upstream branch.This branch has no remote counterpart recorded, so a bare git push or git pull does not know where to go. It happens when you created a branch with git switch -c and never pushed it. git push --set-upstream origin feature creates the branch on the server and records the pairing once, after which plain git push is enough. The command only writes one line of local config and one new branch on the server, so it deletes nothing and is safe to repeat.error: cannot lock ref 'refs/remotes/origin/main': is at 8a3f21c but expected 1c2d3e4As git went to write a new value into a remote-tracking ref, it found a value other than the one it had just read, so it refused to overwrite. It commonly happens when your editor fetches in the background while you fetch too, or when someone force-pushed and your remote-tracking refs are out of step. Running the fetch again usually just works. git remote prune origin tidies away remote-tracking refs whose branches no longer exist upstream; it deletes only the copies inside your own repository and never touches a branch on the server.fatal: not a git repository (or any of the parent directories): .gitThere is no .git in this directory or in any directory above it, so git could not find a repository. It happens when you are standing one level above or below the repository, when a clone landed in a different folder, or when you never ran git init. Check where you are with pwd first, and use git init only if you truly mean to start a repository here. Running git init inside a subfolder of an existing repository creates a second, nested repository that quietly shadows the outer one, and from then on the files in that folder never enter the outer commits.fatal: remote origin already exists.The name origin is already in use in this repository, so it cannot be created a second time. A cloned repository has origin from the start, and the message appears when you follow a fresh-repository guide inside one and run git remote add origin. Check where origin points with git remote -v first; if what you wanted was a different URL, git remote set-url origin changes only where it points. This setting is one line in .git/config, reversible at any time and with no effect on your commits.error: pathspec 'featue' did not match any file(s) known to gitgit found no file, branch or tag by that name — the string inside the quotes is exactly what it looked for. It happens on a typo, when the branch exists only on the server and you have not fetched, or when the file sits in a different directory than you assumed. For a branch, git fetch then git branch -a and compare the name by eye; for a file, git status --short shows the real paths. Remember that paths in git commands are read relative to the directory you are standing in, not the repository root, which alone accounts for many of these.git@github.com: Permission denied (publickey).ssh reached the server but offered no key the server is willing to accept — this is authentication, not networking. It happens when you have not generated a key, or you have one but never added it to ssh-agent, or the public key is not registered on your account with that host. ssh-add ~/.ssh/id_ed25519 loads the key into the agent for this session, and ssh -T git@github.com reports which key was tried and who you are recognised as. Both commands only read; there is nothing to lose.remote: Support for password authentication was removed on August 13, 2021.GitHub stopped accepting account passwords over https in 2021 — your password is not wrong, the method itself is no longer accepted. It appears when your stored credential is an old password, or when you typed one at the prompt. To stay on https, put a personal access token in the password field; otherwise switch to the ssh address with git remote set-url origin. Changing the remote address is one line of config in your own clone, so it touches no commits and is reversible at any time.fatal: Authentication failed for 'https://github.com/user/repo.git/'The credential that was sent got rejected — it is wrong, expired, or a token without the scope this repository needs. The common trap is an expired token still cached by your credential helper: it is sent without ever prompting you, so you get the same line every time. The git credential reject line in the fix removes just that stored entry so you are asked again and can paste a fresh token. It deletes only the saved credential and touches no repository data.fatal: Unable to create '/repo/.git/index.lock': File exists.git creates .git/index.lock as a lock while it writes the index, so the file already being there means another git is running or one died and left it. It is left behind by an editor or IDE running git in the background, or by a command you interrupted with Ctrl+C or killed. Make sure no git is running, then remove it with rm -f .git/index.lock. Deleting it while a git process is genuinely at work can corrupt the index, so check first; if the index does end up wrong, git reset rebuilds it from HEAD, and without --hard it leaves your files alone.nothing to commit, working tree cleanThis is not an error: git is telling you nothing differs from the last commit, so it did nothing. You see it when the file you edited is matched by .gitignore, when the place you edited is a different clone or worktree from the one you are standing in, or when you already committed and forgot. git check-ignore -v <path> prints the exact .gitignore line that hides a file, and git log -1 shows whether the change already landed. Both commands only read.error: unable to unlink old 'dist/main.js': Permission deniedA checkout wanted to replace a file with a new version and the operating system refused to remove the old one. It happens when another program holds the file open, which is especially common on Windows, or when you do not have write permission on the directory. Close whatever holds it — dev server, editor, antivirus — and run the same command again; on Unix, fix the directory permission. Repeating the command is safe, but note that git stopped partway, so the working tree stays half-updated until it succeeds.fatal: bad object 8a3f21cThe name you gave does not resolve to anything git can read: either the object is not in this repository, or it is there and damaged. It happens with a hash copied from another clone or from a shallow clone, with a truncated hash that is too short, or with a genuinely corrupted object after a disk problem. git fsck --full reports missing and broken objects read-only, and a hash from someone else needs a git fetch before the object exists here. If fsck does report corruption, re-cloning is faster and more certain than repairing — just copy your uncommitted files somewhere safe first.warning: LF will be replaced by CRLF in package.json.This is a notice, not an error: core.autocrlf is on, so git stores LF in the commit but will write CRLF into your working copy. Installing git on Windows with the defaults sets autocrlf to true, so it appears on every add on that machine. git config core.autocrlf input stores LF and stops converting on checkout, and the better answer is a .gitattributes in the repository containing * text=auto eol=lf, so every clone behaves the same. Changing the setting can make every file look modified once; that is a single re-checkout, not lost work.The file will have its original line endings in your working directoryThis is the second half of the CRLF notice above: it says the file on disk keeps whatever line endings it already has, and only the copy stored in the commit is normalised. It comes from the same autocrlf setting, so nothing here is broken by itself. But if diffs show whole files changed, that is a sign your team's line endings differ, so pin the rule with a .gitattributes in the repository and run git add --renormalize . once. That command rewrites only how endings are stored; it does not change file content.husky - pre-commit hook exited with code 1 (error)Your commit was never created: a hook — lint, tests, a formatter — exited non-zero and git aborted. The real reason is not this line but the hook's own output above it, usually a lint rule or a type error. Fixing what the hook reported and committing again is the only real answer. git commit --no-verify skips every commit hook and does produce a commit, but it has not passed the check, it has only moved the failure to CI, and unformatted code goes straight to your colleagues.

npm20

With npm the cause sits in the first code XXXX line rather than the last six npm ERR! lines, and when the failure comes from the dependency tree or a native build instead of your own code, deleting node_modules and installing again clears about half of them.

npm ERR! ERESOLVE unable to resolve dependency treeFrom npm 7 onward peerDependencies ranges are enforced, and this says npm could not find a set of versions that satisfies all of them at once. It is common when the package you are installing has a peer range that excludes the react or typescript version you already have, especially right after a major upgrade. Read the Found: and Could not resolve: lines to see who demands what, then move one of the two to a compatible version — that is the real fix. npm install --legacy-peer-deps installs by ignoring peer ranges entirely, which unblocks you but leaves a tree the packages never agreed on, so the runtime errors from mismatched versions are yours to debug.npm ERR! Conflicting peer dependency: react@18.3.1This line picks out the actual clashing pair inside the ERESOLVE report: this package demands that version of a peer while your tree holds a different one. It usually means the host library had a major bump and a plugin that uses it has not caught up. Asking by name with npm ls react prints, as a tree, who required which version, and which of the two you have to move becomes obvious on the spot. Upgrading the plugin is the real fix; --force writes the mismatched tree anyway and hides the problem until runtime.npm WARN react-dom@17.0.2 requires a peer of react@17.0.2 but none is installed.npm 6 only warned about peer dependencies and never installed them for you: the package is present, but the companion it says it needs is not. It appears in a project still on npm 6, or when you are using an old lockfile made in that era. Install the named peer yourself at a version inside the printed range and it is done. Nothing has failed yet because this is only a warning, but it comes back later as Cannot find module, or as two copies of the same library producing errors that make no sense.npm WARN deprecated request@2.88.2: request has been deprecatedThe package's author marked that version as no longer recommended: it installed and it still works. It is usually not something in your own package.json but a transitive dependency dragged in by a package you do use. Asking by name with npm ls request shows which of your direct dependencies pulls it in, and the place to fix is that direct dependency, not this package. There is nothing to do today — deprecated is not a security vulnerability, and npm audit is what tells you about those.npm ERR! npm ci can only install packages when your package.json and package-lock.json or npm-shrinkwrap.json are in sync.npm ci installs strictly what the lockfile records, and it stopped before starting because package.json asks for something the lockfile does not contain. It happens when someone hand-edited package.json or resolved a conflict in it without running install, or committed package.json without the lockfile beside it. Run npm install locally once so the lockfile matches, then commit the lockfile — that is the whole fix. Deleting package-lock.json to get past it instead re-resolves every dependency to newer versions and quietly changes what your build contains.npm ERR! The `npm ci` command can only install with an existing package-lock.json or npm-shrinkwrap.jsonnpm ci does not run at all without a lockfile, because it has nothing telling it which versions to install. It happens when package-lock.json is in .gitignore, was never committed, or when the directory you are standing in is not the project root. npm install --package-lock-only writes the lockfile without installing anything, and committing it makes CI work. Never put package-lock.json in .gitignore: the guarantee that CI builds the same thing twice rests on that one file.npm ERR! Invalid Version:The version field in package.json is not valid semver, so npm cannot parse the package at all. It happens with a hand-edited value like 1.0 or v1.0.0 or an empty string, especially after a badly resolved conflict in that file. npm pkg set version=1.0.0 writes a proper three-part version back. The command edits only package.json and touches neither node_modules nor the lockfile, so every command that reads the file works again immediately afterwards.npm ERR! 404 Not Found - GET https://registry.npmjs.org/@acme/ui - Not foundThe registry has no package by that name — a 404 is an answer about the name, not about your network or your credentials. It happens on a typo, on a package that was unpublished, or on a private scope you are not logged in to. A private package looks exactly like a nonexistent one to someone without access, which is why all three collapse into this single line. npm view @acme/ui version confirms whether the name exists publicly, and for a private scope check that .npmrc has the registry and token for that scope. Both checks only read.npm ERR! request to https://registry.npmjs.org/express failed, reason: unable to verify the first certificateThe TLS connection failed: the certificate chain the server presented leads up to an authority node does not trust. Overwhelmingly this is a corporate network whose proxy opens traffic and re-signs it with its own certificate; occasionally it is a server that failed to send its intermediate certificate. Teaching npm your company's root with npm config set cafile is the correct fix. strict-ssl false also makes this line disappear, but it turns certificate checking off altogether, which lets anyone on the path serve you altered packages — do not use it.npm ERR! code EINTEGRITYThe hash of the tarball npm downloaded does not match what the lockfile records, so npm decided it cannot trust what arrived and stopped. It is a corrupted cache entry, or a proxy that modified the download, or more rarely a lockfile whose integrity value was hand-edited or badly merged. npm cache clean --force empties the local cache so the next install re-downloads; it deletes only cached copies and is safe to repeat. If it keeps happening for one package, the recorded integrity itself is wrong and that entry has to be re-resolved with npm install.npm ERR! Error: EACCES: permission denied, access '/usr/local/lib/node_modules'npm tried to write into a system directory your user does not own — the package is fine, you simply have no right to write there. It is almost always npm install -g against a node that the operating system's package manager put in a place like /usr/local. npm config set prefix ~/.npm-global moves global installs into your home directory, and once its bin is on PATH the error does not come back. sudo npm install -g does work, but it leaves root-owned files in your cache and node_modules that produce the same EACCES on ordinary installs later; a version manager such as nvm removes the whole class of problem.npm ERR! enoent ENOENT: no such file or directory, open '/home/me/package.json'npm looked for package.json in the current directory and up the tree and found none — you ran an npm command somewhere that is not a project. It happens when you stand at the root of a monorepo while the project is in a subfolder, when you stand next to the folder you cloned, or when you simply forgot to cd. Moving to the folder that has package.json is the answer, and an ls is the quickest way to see it. Use npm init -y only if you genuinely mean to start a new project here: run in the wrong folder it leaves a stray package.json that confuses tooling later.npm ERR! code ELIFECYCLEA script in package.json exited with a non-zero code — ELIFECYCLE is npm's wrapper around that, not the cause. The cause is whatever your build or test script did, and the real error lines sit above this one. Scroll up to the first error, or run the actual command printed after npm ERR! <pkg>@<ver> <script>: by hand to see its output without npm's wrapper around it. The exit code that follows tells you a little too: 1 is a plain failure, while 137 means the process was killed, typically for memory.gyp ERR! build errorA dependency contains C or C++ that has to be compiled on your machine at install time, and that compile failed — node-gyp needs a compiler and python. It happens when no build toolchain is installed, or when the package is older than your node and the headers no longer match. Install the toolchain: xcode-select --install for the command line tools on macOS, build-essential and python3 on Debian or Ubuntu, the Visual Studio C++ workload on Windows. If a newer version of the package ships a prebuilt binary for your node, upgrading the package is far quicker than fixing the build environment.Error: Cannot find module 'express'node could not resolve that name to any file: it is not in node_modules, or you ran from a directory that has no node_modules. It happens when you never installed, when an install failed halfway and left a partial tree, or when the package is in devDependencies and you installed with --omit=dev. rm -rf node_modules && npm install rebuilds the tree from the lockfile; it is safe, loses nothing, and costs only the download time. But if the name in the quotes is a relative path starting with ./, nothing is missing from your packages — the path in your own code is wrong.Module not found: Error: Can't resolve './components/Button' in '/app/src'The bundler could not find that file at that path — this is your own import, not a package. It happens when the relative path is off by one level, when the extension is missing or wrong, or when the filename's capitalisation differs from the import. That last one is the nastiest: the macOS and Windows filesystems are case-insensitive, so it works on your machine and breaks only on Linux CI. ls the directory and compare it with the import character by character, capitalisation included. If the name is a package rather than a path, install it; either way nothing gets deleted.Error: error:0308010C:digital envelope routines::unsupportednode 17 shipped OpenSSL 3, which dropped an old hash algorithm from the defaults, and a tool that still asks for it falls over right inside that hash call. It is almost always an old webpack 4, or a tool that bundles one, running on a modern node. export NODE_OPTIONS=--openssl-legacy-provider re-enables the legacy algorithms for that process and is harmless for a build. But it is keeping a dead tool alive: upgrading to webpack 5, or to the current version of your framework, removes the need for the flag entirely.FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memoryThe V8 heap hit its ceiling and node gave up on its own — the operating system did not kill it, node decided it could not grow further and stopped. The cause is a big type-check or bundle, a build with source maps on a large project, or leaking code such as accumulating everything in an array. export NODE_OPTIONS=--max-old-space-size=4096 raises the ceiling to 4 GB and is often enough, but the machine has to actually have that much RAM, and if a container's limit is lower the operating system kills the process instead, which shows up as exit code 137. If the number keeps having to grow, the cause is a leak and not the limit.npm WARN EBADENGINE Unsupported engineSome package declares an engines range for node or npm that your environment does not satisfy — npm only warns and installs anyway. It happens when you are on an old node installed as a system package, or when the project moved to a newer node than the one in your shell. Switch node to a version inside the printed range with your version manager; the thing to trust is the project's .nvmrc or the engines field in package.json. It is only a warning by default, but with engine-strict=true in .npmrc the same condition becomes an error that stops the install.zsh: command not found: tscThe shell searched PATH and found no executable by that name — the install itself may well have succeeded. It happens when npm's global bin directory is not on PATH, when you installed locally rather than globally so the binary went into node_modules/.bin, or when the shell is holding an old PATH in its cache. npx tsc --version runs the local copy without touching PATH at all, so that one line separates not installed from not on PATH. For a genuinely global install, add the output of npm prefix -g plus /bin to PATH and open a new shell.

JavaScript15

Browser and Node errors usually tell you what broke and not why the value became what it was — you read undefined, it was not a function, it was not JSON — so the place to fix is upstream of the line that threw, and silencing just that line with ?. and default values makes the same problem reappear further away in a form that is harder to recognise.

Uncaught TypeError: Cannot read properties of undefined (reading 'name')The value to the left of the dot is undefined and you read a property off it; the name in parentheses is the property you wanted, so the broken thing is the value before it. It comes from a response that has not arrived yet, a prop nobody passed, one level too deep as in data.user.name, or arr[0].id on an empty array. Where the value is legitimately optional, user?.name short-circuits it — but if the value was supposed to be there, ?. only converts the error into an undefined that fails on the next line, so find upstream why it is undefined. Before Chrome 78 the same error read Cannot read property 'name' of undefined.TypeError: items.map is not a functionThe name exists but it is not a function — if it did not exist at all, the message would talk about undefined instead. Typical causes are something you took for an array that is really an object or a NodeList, an API that returns {data: [...]} rather than the array itself, or the wrong shape of default export from a module. Print it before you theorise: one console.log(typeof items, items) settles half of these on the spot. A NodeList or Set only needs Array.from(items); but if it was {data: [...]}, wrapping it is wrong and items = res.data is the fix.RangeError: Maximum call stack size exceededCalls piled up deeper than the engine allows, and it is usually not deep recursion but endless recursion. A function whose stopping condition is missing or never reached, two functions calling each other, JSON.stringify on an object that contains itself, or in React a render that sets the state it depends on. The stack in the console repeats two or three frames — that pair is the loop, and the base case belongs there. When the computation genuinely needs depth, rewriting the recursion as a loop or an explicit stack is the only route: you cannot raise the stack limit in a browser.SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSONWhat you handed to JSON.parse was HTML, not JSON. The <!DOCTYPE fragment quoted in the message is the proof, and it means the server answered with an HTML page — a 404, a 502, or a login redirect — so the real bug is upstream of the parse, in the request. Typical causes: the wrong URL, a dev-server proxy that served index.html instead of the API, or an expired session that redirected you to a login page. Check res.ok before calling res.json(), and read res.text() to see what actually arrived; hitting the URL with curl -s is the fastest way to look. Before Chrome 111 and Node 20 the same error read Unexpected token < in JSON at position 0.SyntaxError: Unexpected end of JSON inputThe parser reached the end of the document while still reading JSON, and almost always the body was simply empty. Calling res.json() on a 204 No Content or on an error response with no body, reading a response body twice so the second read comes back empty, or reading a file that was truncated mid-write. Take the text first and check it before parsing — const t = await res.text(); if (!t) return null; — which gives you an exact diagnosis. Papering over it with JSON.parse(text || 'null') makes an empty response look normal, so find out why the server sent an empty body first.TypeError: Failed to fetchThe request ended without a response, and the browser deliberately hides the reason: a CORS block, a dead address, a bad certificate, and an ad-blocking extension all produce these same two words. This is a thrown error rather than a status, so a 404 or a 500 never reaches it — the server never got as far as answering. There is often a second, more specific line just above it in the console or in the Network tab, so read that first, and hitting the URL with curl -i separates "the server is down" from "the browser refused". Firefox words the same situation as NetworkError when attempting to fetch resource.Access to fetch at 'https://api.example.com/data' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.The request reached the server and a response came back, but that response carried no header allowing this origin, so the browser refused to hand it to your JavaScript. Only browsers enforce this rule, which is why the same URL works from curl — it looks as though the server is fine and only the browser is broken. The fix always lives on the server: add Access-Control-Allow-Origin: http://localhost:3000 to the response. There is nothing the front end can do, and if the server is not yours, proxying the call through your own is the only route. Allow-Origin: * does not work for requests that send cookies; those need the exact origin plus Allow-Credentials.SyntaxError: Cannot use import statement outside a moduleNode or the browser read that file as a CommonJS script, and it contained the ESM keyword import. A .js file is CommonJS unless package.json declares otherwise, and in a browser the same thing happens when the <script> tag has no type="module". npm pkg set type=module declares the whole package as ESM and clears it, but from that moment every .js in the package loses require and __dirname, so the remaining CommonJS files must move too — if you only want to convert one file, renaming it to .mjs is the narrower and safer change.ReferenceError: require is not defined in ES module scope, you can use import insteadThis is the exact mirror of the previous error — the file is being read as ESM and it contains CommonJS's require. It happens almost always right after adding "type": "module" to package.json while old scripts are still in the tree. To leave that one file in the old world, mv script.js script.cjs is the narrowest change; to move it forward, rewriting require as import also means dealing with __dirname and require.main === module, which do not exist in ESM.Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/app/src/util' imported from /app/src/index.jsWhen Node runs ESM it treats import paths as literal file names the way a browser does, so './util' is not './util.js' — it is a file that does not exist. CommonJS used to try extensions for you, and TypeScript leaves extensionless imports untouched through compilation, which is why a tsc-built project throws a pile of these the first time you run it with node. Add the .js extension to relative imports; inside a TypeScript file you still write './util.js', because the compiled output is what Node will read. Package names from node_modules take no extension.ReferenceError: window is not definedThe code ran inside Node on the server rather than in a browser. Node has no window, no document and no localStorage, so touching window while a module is being loaded in a framework that server-renders first, such as Next.js or Nuxt, stops here — often it is a single line outside the component, or the import of a browser-only library itself. If the work is browser-only, guard it with typeof window !== 'undefined', or better, move it inside useEffect, which only ever runs in the browser. When a whole library is browser-only, Next.js can exclude it from the server render with dynamic(() => import('./C'), { ssr: false }) — at the cost of that part being absent from the server-rendered HTML.Hydration failed because the initial UI does not match what was rendered on the serverThe HTML the server sent and what the browser produced on its first render differ, so React could not attach one to the other. Using new Date() or Math.random() during render, reading localStorage, or drawing differently based on window width guarantees two different answers — and so does markup the browser silently repairs, such as a <div> inside a <p>. The rule is to render exactly what the server rendered on the first pass and change it after useEffect has run; the cost is that the differing part appears a moment later and is absent from the server HTML. React 19 words the same situation as "the server rendered HTML didn't match the client" and prints a diff of what differed.Objects are not valid as a React child (found: object with keys {name, id})The value you asked React to render is an object rather than a string or a number, and the list of keys in parentheses tells you which object it is. Usually the line should read {user.name} where it says {user}, or you tried to render a whole response. If you see found: [object Promise] instead of a key list, the cause is different: you rendered the result of an async function without awaiting it. Picking the field to display is the fix; JSON.stringify(user) is fine for a quick look while debugging, but do not leave it on screen.Each child in a list should have a unique "key" prop.Sibling elements rendered as a list have no key, so React cannot match them up with the same items on the next render. You built the elements with items.map(...) and left key out; it is only a warning, so the screen still draws, but it comes back as a quiet bug once the list changes — the symptom is an input value or an animation staying on the wrong row. Give it a stable id from your data. Using the array index as the key is safe only for a list whose order never changes and where nothing is inserted or removed in the middle; otherwise it reproduces almost exactly the problem of having no key at all. As of React 19 the leading Warning: is gone.Too many re-renders. React limits the number of renders to prevent an infinite loop.A render set state, that state triggered another render, and React cut the loop to stop an infinite one. Nearly always this is a function being called where it should have been passed: onClick={setOpen(true)} runs on every render, and it has to be onClick={() => setOpen(true)}. The other common shapes are setting state directly in the body of the render, and a useEffect whose dependency list contains the value it updates. Move state changes into an event handler or a useEffect, and if the value can simply be computed during render, ask whether it needs to be state at all.

Python18

A Python traceback splits the answer in two — the last line says what went wrong, the frames above it say where — so reading only the last line gives you the name and loses the place, and for the value-is-missing errors such as NoneType and KeyError the cause almost always sits in a frame above the one that crashed.

ModuleNotFoundError: No module named 'requests'Python searched every directory on sys.path and found no module with that name. Either it was never installed, or it was installed for a different interpreter — pip install outside a virtual environment and then running inside one produces exactly this. Writing it as python -m pip install installs into the interpreter you are actually running, which removes the mismatch.error: externally-managed-environmentThis Python belongs to the operating system or to Homebrew, and pip refuses to write into it (PEP 668). You ran pip install against the system interpreter; the message is telling you to create a virtual environment. --break-system-packages does what its name says — it can leave apt-managed files in a broken state — so make a .venv and install there instead.ImportError: cannot import name 'User' from partially initialized module 'models' (most likely due to a circular import)Two modules import each other, so you asked for a name from a module that is only half-loaded. It comes from an A imports B, B imports A shape, and it is common right after splitting a file when one type hint pulls an import back the other way. Change from y import Thing to import y and use y.Thing inside the function: the lookup happens later, and the cycle stops mattering.IndentationError: unexpected indentThe line is indented further than the one before it, and nothing above it opened a block that would justify the extra level. Usually you pasted code that carried its own leading spaces, or you deleted an if line and left its body indented under nothing. Remove the leading whitespace on the line the error points at; if you cannot see the difference, print that line through cat -A and the spaces and tabs become visible.TabError: inconsistent use of tabs and spaces in indentationTabs and spaces are mixed inside one block, so Python cannot decide which line is deeper. It happens when you paste from an editor that inserts tabs, or when two people edit the same file with different settings; on screen the lines look identical, so you will not find it by eye. python -m tabnanny app.py prints the offending line numbers, and the rest is converting that file to four spaces throughout in your editor.SyntaxError: invalid syntaxThe parser could not make a statement out of what it found there, and the cause is usually the line before the one it points at rather than that line itself. An unclosed bracket or quote, a missing colon, or Python 2's print "x" are the common causes — with an unclosed bracket the parser keeps going for several lines before giving up. python -m py_compile app.py checks the syntax without running anything, and since 3.10 the messages are far more specific, so upgrading Python is itself a diagnostic.TypeError: 'NoneType' object is not subscriptableThe value you tried to index with [...] is None. Something upstream returned None quietly: dict.get() on a missing key, a re.match that did not match, or a function with no return statement on the path that ran. Rather than guarding at the crash site, walk upstream and find why it is None — an if that skips over None just moves the same error to the next line.AttributeError: 'NoneType' object has no attribute 'get'The object to the left of the dot is None, so the attribute or method you asked for does not exist. It shares its root with nonetype-not-subscriptable and shows up especially when you chain straight off a function that returns None on failure, such as BeautifulSoup's find() or re.search(). Print what that function was looking for first, and if finding nothing is a legitimate case, handle that branch explicitly.KeyError: 'name'The key is not in the dict, and the string in quotes is exactly the key it looked for — start there, because it is usually a typo, a case difference, or an API response whose shape is not what you assumed. If the value is genuinely optional, d.get("name") returns None instead and d.get("name", 0) supplies a default; but using .get on a value that must be there trades the error for a None that travels into your code and fails much further away.IndexError: list index out of rangeYou asked for a position the list does not have. With length n the last valid index is n-1, so range(len(x) + 1) and x[len(x)] always stop here, and x[0] on an empty list is the same error — common when a filter or split above it left nothing behind. Looping as for item in items: removes this class of bug outright, and when you truly need the position, enumerate(items) gives it to you.ValueError: invalid literal for int() with base 10: '3.5'You handed int() a string it cannot read as a whole number, and the text in quotes is that string. A decimal point as in '3.5', an empty string, an input with a trailing newline, or a thousands separator as in '1,000' are the usual causes. For a decimal, int(float(s)) works in two steps — but it truncates the fraction away rather than rounding — and for anything a person typed, wrapping it in try/except ValueError is the honest fix.UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byteYou read a file or byte string as UTF-8 and hit a byte that UTF-8 does not allow; the message names both the position and the byte. Usually the file is cp1252 or another legacy encoding out of Windows, or it is not text at all — an image, a zip, or gzip data you forgot to decompress. Finding the real encoding and passing it as encoding= is the correct fix; errors="replace" lets the read finish but turns those bytes into question marks, so the data is silently damaged from then on.ZeroDivisionError: division by zeroSomething was divided by zero — and in practice the divisor is rarely a literal zero, it is a count that came out zero. Averaging code where the list turned out empty, or a filter that matched nothing, is the classic source: it passes on your development data and first fails in production. Check the denominator before you divide and decide what zero means there; returning 0, returning None, and letting the exception propagate are three different decisions, and each is right in some situations.RecursionError: maximum recursion depth exceededA function called itself deeper than the default limit of 1000 frames. Far more often than a genuinely deep computation, this means the stopping condition is missing or never reached; indirect recursion counts too, such as a __getattr__ or a property that reads its own attribute again. Find the two or three frames that repeat in the traceback and fix the base case first. sys.setrecursionlimit() does raise the ceiling, but it lets you overrun the real C stack, and then Python dies outright instead of raising — rewriting the recursion as a loop is the safe answer.UnboundLocalError: cannot access local variable 'count' where it is not associated with a valueIf a function assigns to a name anywhere in its body, Python treats that name as local to the function — and you read it before the assignment ran. It shows up when you expected the outer value of the same name to be visible, or the first time you write a read-and-write operation such as count += 1. Initialising it inside the function with count = 0 is the usual answer; if you really must change the module-level value, global count does that, at the cost of a value whose writers become hard to trace. Up to 3.10 the same error read 'local variable referenced before assignment'.TypeError: greet() takes 1 positional argument but 2 were givenYou passed one more argument than the function accepts, and when the numbers differ by exactly one it is almost always self. Define a method inside a class as def greet(name): and calling obj.greet("x") sends obj as the first argument, which makes two. If it is a method, change it to def greet(self, name): so it accepts self; if it does not need the instance at all, mark it @staticmethod.PermissionError: [Errno 13] Permission deniedThe operating system refused that operation on that path. You were writing into somewhere owned by someone else such as /var/log or /usr, or a mounted volume in a container whose UID does not match the container user, or — the one people miss — you have no write permission on the directory rather than on the file, which is what creating a new file needs. Check owner and mode with ls -ld first, and before reaching for sudo consider moving the path to somewhere you can write: a file created under sudo raises the same error the next time you open it without sudo.OSError: [Errno 98] Address already in useThe bind failed because another process already holds that port. Either the previous server did not fully die, an auto-reloader started two copies, or a Docker container is already publishing the same port. lsof -i :8000 gives you the PID that holds it so you can clear just that one — read the process name before you kill -9 anything. The errno differs per system: Linux prints 98 here, macOS prints 48.

Build and types10

Build errors are a tool refusing before anything runs, so they break nothing at the moment they appear — but they do ask two questions: will you fix the type or the path to match the real shape, or will you push that one line through with an as any or a suppression comment — and it is here that the things which are only true on your own machine, letter case, extensions and environment variables, show themselves for the first time.

error TS2307: Cannot find module 'lodash' or its corresponding type declarations.tsc found neither the module itself nor any type declarations for it, and it matters that the message says both — the package may be missing entirely, or installed but shipping no types. If it is installed, the types are what is missing: npm i -D @types/lodash adds the companion package, though many modern packages bundle their own types and have no @types at all. When this fires on a relative import instead, look at letter case and at the paths in tsconfig — a path into your own files has nothing to do with @types.error TS2339: Property 'user' does not exist on type 'Request'.That type does not declare that property. It comes from adding a field the library's type does not have (attaching user to Express's Request is the classic), from a typo, or from reading a property that exists on only one member of an un-narrowed union. Fixing the type to match the real shape is the answer, and for a union, narrowing with 'x' in y or a discriminant field opens access inside the branch. Silencing it with as any only gags the compiler: that spot will now also accept a misspelt property name.error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.The type the function accepts and the type you passed are different, and the message states them in order — the first is what you gave, the second is what it wanted. The most common case by far is a value from a form field, a URL query string or JSON arriving as a string where a number is expected. Converting once at the boundary is the answer, but Number(value) returns NaN rather than throwing when it fails, so you have to stop that NaN from travelling — Number.isFinite(n) is the check that belongs there.error TS18048: 'user' is possibly 'undefined'.The value may be undefined and you have not handled that possibility; this is tsc catching a future Cannot read properties of undefined for you. It shows up on optional fields, on the result of an array find(), and on environment variables that might not be set. Deciding what happens when it is missing is the fix: cut early with if (!user) return null, or short-circuit with user?.name. Silencing it as user!.name is a declaration that you guarantee it — and when the guarantee is wrong it comes back as a runtime exception, with none of the safety a check would have left behind.error TS7006: Parameter 'req' implicitly has an 'any' type.The parameter has no type written on it and tsc had nothing to infer from, so it became any implicitly — which noImplicitAny treats as an error. It shows up in files ported from JavaScript and when a callback is pulled out into its own variable; by contrast, a callback written inline as items.map(x => ...) has context, so tsc infers it and this never fires. Writing the type there is the fix. You can also write any explicitly, but that is a decision to turn checking off for that parameter entirely — when you do not know what it is, unknown is more honest and forces you to narrow before use.Parsing error: Unexpected tokenESLint stopped while reading the syntax, before any rule was applied to the file. Far more often than actually broken code, this means the parser does not know that syntax — a TypeScript file read by the default parser, JSX that was never enabled, or decorators and very new syntax handed to an old parser. Running npx eslint --print-config app.ts against that exact file prints which parser and parserOptions really apply, so start there instead of guessing. TypeScript needs @typescript-eslint/parser, and if the file should not be linted at all, listing it under ignores is the right answer.You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.This is the line webpack adds after Module parse failed, and it means it tried to read that file as JavaScript and the file was not JavaScript. Either you imported something that needs transforming — TypeScript, JSX, CSS, an image, a .vue file — with no rule for it in module.rules, or a package in node_modules ships uncompiled source while that folder sits in your exclude. Adding a loader rule for that extension in module.rules is the fix, and the path printed above the message tells you which file stopped it. Deleting exclude: /node_modules/ to make it go away slows the whole build noticeably; carving out just that one package is the better trade.Failed to resolve import "./utils" from "src/main.ts". Does the file exist?Nothing was found at that path, and the "Does the file exist?" it prints points at the three things actually worth checking: letter case, the extension, and any alias in tsconfig or vite.config. Filesystems on macOS and Windows ignore case, so ./Utils works fine on your machine and breaks for the first time in CI or on deployment, where Linux does care — this error is the most common identity of "but it works locally". git ls-files src | grep -i utils shows the spelling the repository actually holds. For an alias such as @/utils, resolve.alias in vite.config and paths in tsconfig must agree; fix only one and your editor stays quiet while the build keeps failing.You're importing a component that needs useState. This React hook only works in a client component.In the App Router every component is a server component by default, and a server component imported a file that uses a hook which only means something in a browser, such as useState. The directive is a single 'use client' line at the very top of the file, and adding it pulls that file and everything it imports into the client bundle — which is why marking the smallest piece that needs state, rather than the whole page, is the cheap choice. In the other direction, splitting so that a client component receives server components as children keeps the data fetching on the server. Next.js 13 worded the same situation as "It only works in a Client Component but none of its parents are marked with \"use client\"".The engine "node" is incompatible with this module. Expected version ">=20"The package you are installing declares the Node versions it needs in the engines field, and the version you are running is outside that range. The Expected and Got that follow put the requirement and your version side by side, so those two lines are all you need — and if this only happens in CI, its Node version differs from your machine's. Upgrading with nvm install 20 && nvm use 20 is the direct answer, and writing the same version into .nvmrc and your CI config keeps them from drifting apart again. yarn's --ignore-engines will push past it, but it removes the warning rather than creating compatibility: if the package uses newer syntax, it fails at runtime with a syntax error instead. npm reports the same situation as an EBADENGINE warning and does not block the install by default.

Docker12

Docker errors only become readable once you place them in a layer — the client failing to reach the daemon, the registry refusing you, a RUN failing during the build, and a container dying the instant it starts are four different problems — and for the build and run layers the line itself is not the reason: the reason is in the output of the command that was running inside, while the cost of each fix differs by layer too.

Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?The docker command does nothing itself — it asks a daemon over that socket, and nothing answered. Either the daemon is not running, Docker Desktop has not finished starting on macOS or Windows, or on Linux your user is not in the docker group and may not open the socket; all three produce this one line. On Linux start it with sudo systemctl start docker; with Desktop, launch the app first. If it is the permission case, sudo usermod -aG docker $USER and a fresh login fix it — but be aware that group is effectively root-equivalent.Bind for 0.0.0.0:8080 failed: port is already allocatedYou asked for that host port with -p, and another container or process already holds it. Most often a container you started earlier without --rm is still there in a stopped or running state, or compose is still holding an older container. docker ps --filter publish=8080 points straight at the container publishing it; if it turns out to be an ordinary host program rather than a container, lsof -i :8080 finds it. Changing only the host side, as in -p 8081:80, gets you moving, but leaving the old container in place means the same collision next time.Conflict. The container name "/api" is already in use by containerThe name you passed with --name is already taken by another container. Names are unique across stopped containers as well as running ones, so usually a container that died yesterday still holds it — invisible in docker ps and visible only in docker ps -a. docker rm -f api frees the name, and it also destroys that container's writable layer along with it (named volumes survive). For a container you only need once, starting it with docker run --rm avoids the situation entirely.failed to register layer: Error processing tar file(exit status 1): no space left on deviceThere is no room on the disk to unpack the image layer. Usually the project is not large; Docker is simply still holding months of old images, build cache, and volumes from containers you removed. docker system df shows how much sits in images, containers, volumes and build cache and how much of it is reclaimable, so read that before deleting anything. docker system prune removes stopped containers, dangling images, unused networks and the build cache — the next build is noticeably slower once that cache is gone — and adding --volumes also deletes volumes not attached to a container, which is exactly where people lose their databases.pull access denied for myapp, repository does not exist or may require 'docker login'The registry would not show you that repository, and the important part is that the message covers two causes at once: the name does not exist, or it exists and you are not allowed to see it. You are not logged in for a private repository, or the name is missing its user or organisation (myapp and myorg/myapp are different repositories), or the name is simply wrong. For a private image, docker login against that registry; if it is supposed to be public, re-read the spelling. Registries deliberately do not distinguish "absent" from "forbidden", because the existence of a name is itself information.manifest for myapp:v2 not found: manifest unknownThe repository was found but the tag is not in it. Unlike pull access denied, this message tells you the name was right, so the tag is all that is left to check — a tag that was deleted or moved, a pipeline that pushed latest but never created v2, or a tag that has no image for your architecture. docker manifest inspect myapp:v2 confirms whether the tag resolves at all and which platforms it carries. Switching to latest to get past it costs you a build that cannot be reproduced, so fix the tag instead.unauthorized: incorrect username or passwordThe registry rejected the credentials you sent. More often than a mistyped password, this is a password sent to a registry that no longer accepts account passwords at all — Docker Hub takes only access tokens once two-factor is on, and the GitHub and GitLab registries have always wanted a token or deploy key. docker logout clears the stored credential, then docker login again with a token. Passing it as echo $TOKEN | docker login -u user --password-stdin keeps the token out of your shell history.failed to solve: process "/bin/sh -c npm ci" did not complete successfully: exit code: 1That RUN line in your Dockerfile exited with a non-zero status. All this line carries is which command failed and with what code; the actual reason is in that command's own output above, which BuildKit tends to fold away once a step finishes. Re-running with docker build --progress=plain prints every step's output in full, and if a cached layer is hiding an older failure, add --no-cache — at the price of rebuilding everything from the first step. exit code: 1 is not a reason, only the fact that the command failed.COPY failed: file not found in build context or excluded by .dockerignoreCOPY can only take files from inside the build context, and the file is not there. The context is the last argument to docker build, so usually you pointed at a parent folder with ../x, or .dockerignore filtered the file out — classic when node_modules or *.env is ignored and then something inside it is copied. The message names both causes, so read cat .dockerignore first and then rewrite the path relative to the context root. Widening the context to fix it means the whole folder gets sent to the daemon, which makes every build slower.exec /usr/local/bin/entrypoint.sh: exec format errorThe kernel did not recognise the header of that executable, and these days it is nearly always an architecture mismatch — an arm64 image built on Apple Silicon being run on an amd64 host, or the reverse. A shell script missing its #!/bin/sh first line produces the same words. Building with docker build --platform=linux/amd64 is the common answer, but it does not remove the mismatch, it papers over it with emulation: anything running through QEMU is several times slower than native. For an image you will keep, building both architectures with buildx is the route that costs nothing at runtime.standard_init_linux.go: exec user process caused: no such file or directoryThe container tried to run its entrypoint and the kernel answered "no such file" — and the trap is that the file is plainly there. The script's line endings are CRLF, so the first line reads as #!/bin/sh\r and the kernel goes looking for an interpreter literally named sh\r; cloning on Windows or leaving git's autocrlf on produces exactly this. dos2unix entrypoint.sh converts that one file (or sed -i 's/\r$//' entrypoint.sh without it), and a .gitattributes line of *.sh text eol=lf stops it coming back. Naming an interpreter in #! that the image does not have — bash in an alpine image — gives the same words.OCI runtime create failed: exec: "bash": executable file not found in $PATH: unknownThe container was created, but the program you asked it to run was not found on $PATH inside it. Alpine-based images ship sh and no bash, so docker run -it myapp bash or a CMD ["bash", ...] ends in exactly this error — as does any tool you assumed was in the image when it only exists on your host. docker run --rm -it myapp sh gets you a shell so you can see what the image actually contains. If you genuinely need bash, RUN apk add --no-cache bash in the Dockerfile adds it, at the cost of a larger image.

Reading an error message

  • Read from the first line down. The lower you go the more it is about the tool’s internals; the cause is usually at the top.
  • If there is a file and a line number, start there — not the top stack frame, but the topmost line that names a file you wrote.
  • Search the message verbatim, but strip your own paths and variable names first; those are what stop the search from matching.
  • The same condition is worded differently across tool versions. If results look wrong, add the version number to the query.
  • Before pasting a fix, check what it throws away. Some of these cannot be undone.

Common questions

Q. Why aren’t the messages translated?

Because you are going to search them. The tool prints English, and a translated message finds nothing. Only the meaning and the remedy follow the language.

Q. The wording on my screen is slightly different.

Tools reword these between versions. If the skeleton matches once you strip your paths and names, it is the same error. When versions differ, add the version number to the query.

Q. Can I just run the fix?

Check what it discards first. git reset --hard, a force push and docker system prune all remove things you cannot get back — the entries say so where that applies.

Q. How do I look up an error that is not here?

Strip your own paths, names and numbers out of the message and search what is left. That remainder is the sentence the tool authors wrote, and it is what matches.