@ai-cortex/daemon 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +64 -0
- package/README.md +217 -0
- package/brain/cortex-brain.yaml +3 -0
- package/brain/entries/bash-permission-denied.md +100 -0
- package/brain/entries/cors-preflight.md +98 -0
- package/brain/entries/docker-port-conflict.md +99 -0
- package/brain/entries/env-vars-missing.md +107 -0
- package/brain/entries/git-merge-conflict.md +88 -0
- package/brain/entries/node-esm-import.md +71 -0
- package/brain/entries/npm-peer-deps.md +93 -0
- package/brain/entries/python-import-circular.md +112 -0
- package/brain/entries/python-import-resolution.md +83 -0
- package/brain/entries/python-venv-activate.md +98 -0
- package/brain/entries/react-stale-closure.md +76 -0
- package/brain/entries/sqlite-locked.md +110 -0
- package/brain/entries/typescript-strict-null.md +93 -0
- package/dist/bin/cortex-statusline.js +3 -0
- package/dist/dashboard/index.html +1616 -0
- package/dist/index.js +3 -0
- package/package.json +44 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: git-merge-conflict
|
|
3
|
+
title: "Git merge conflict resolution"
|
|
4
|
+
language: general
|
|
5
|
+
error_types: ["CONFLICT", "merge conflict", "Automatic merge failed"]
|
|
6
|
+
tags: [debugging, git, merge, version-control]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Problem
|
|
10
|
+
|
|
11
|
+
Git reports `CONFLICT` during merge, rebase, or pull. Automatic merge failed because both branches modified the same lines.
|
|
12
|
+
|
|
13
|
+
## Understanding Conflict Markers
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
<<<<<<< HEAD
|
|
17
|
+
your changes on the current branch
|
|
18
|
+
=======
|
|
19
|
+
incoming changes from the other branch
|
|
20
|
+
>>>>>>> feature-branch
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Everything between `<<<<<<< HEAD` and `=======` is your version. Everything between `=======` and `>>>>>>>` is theirs.
|
|
24
|
+
|
|
25
|
+
## Resolution Steps
|
|
26
|
+
|
|
27
|
+
### 1. See which files conflict
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
git status
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Files marked `both modified` need manual resolution.
|
|
34
|
+
|
|
35
|
+
### 2. Edit the files
|
|
36
|
+
|
|
37
|
+
Open each conflicted file, remove the markers, and keep the correct code. This may mean keeping one side, the other, or a combination.
|
|
38
|
+
|
|
39
|
+
### 3. Mark as resolved and commit
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
git add <resolved-file>
|
|
43
|
+
git commit
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Shortcut: Accept One Side Entirely
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Keep your version for a specific file
|
|
50
|
+
git checkout --ours path/to/file
|
|
51
|
+
|
|
52
|
+
# Keep their version for a specific file
|
|
53
|
+
git checkout --theirs path/to/file
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
For an entire merge:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
git merge -X ours feature-branch # prefer current branch
|
|
60
|
+
git merge -X theirs feature-branch # prefer incoming branch
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Abort If Needed
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
git merge --abort # undo a merge in progress
|
|
67
|
+
git rebase --abort # undo a rebase in progress
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
This restores the repository to the state before the merge/rebase started.
|
|
71
|
+
|
|
72
|
+
## Squash Alternative
|
|
73
|
+
|
|
74
|
+
If the branch has many small conflicting commits, squash merge avoids per-commit conflicts:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
git merge --squash feature-branch
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
This applies all changes as a single uncommitted changeset that you resolve once.
|
|
81
|
+
|
|
82
|
+
### Quick Checklist
|
|
83
|
+
|
|
84
|
+
- `git status` to find conflicted files
|
|
85
|
+
- Edit files to remove conflict markers
|
|
86
|
+
- `git add` resolved files, then `git commit`
|
|
87
|
+
- Use `--ours`/`--theirs` for bulk resolution
|
|
88
|
+
- `git merge --abort` to back out safely
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: node-esm-import
|
|
3
|
+
title: "Node.js ESM import resolution"
|
|
4
|
+
language: javascript
|
|
5
|
+
error_types: [ERR_MODULE_NOT_FOUND, "Cannot find module"]
|
|
6
|
+
tags: [debugging, imports, node, esm]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Problem
|
|
10
|
+
|
|
11
|
+
Node.js throws `ERR_MODULE_NOT_FOUND` or `Cannot find module` when using ES module imports. This happens because ESM has stricter resolution rules than CommonJS.
|
|
12
|
+
|
|
13
|
+
## Root Causes & Fixes
|
|
14
|
+
|
|
15
|
+
### 1. Missing file extensions in relative imports
|
|
16
|
+
|
|
17
|
+
ESM requires explicit file extensions. Node will not auto-resolve `.js` or `.ts`:
|
|
18
|
+
|
|
19
|
+
```javascript
|
|
20
|
+
// WRONG
|
|
21
|
+
import { helper } from './utils';
|
|
22
|
+
|
|
23
|
+
// CORRECT
|
|
24
|
+
import { helper } from './utils.js';
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
In TypeScript projects, use `.js` extensions even for `.ts` source files — TypeScript does not rewrite import paths and Node resolves the compiled `.js` output.
|
|
28
|
+
|
|
29
|
+
### 2. package.json missing `"type": "module"`
|
|
30
|
+
|
|
31
|
+
Without this field, Node treats `.js` files as CommonJS:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"type": "module"
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Alternatively, use `.mjs` extension for ES module files without changing package.json.
|
|
40
|
+
|
|
41
|
+
### 3. TypeScript config
|
|
42
|
+
|
|
43
|
+
Set these in `tsconfig.json` for ESM output:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"compilerOptions": {
|
|
48
|
+
"module": "NodeNext",
|
|
49
|
+
"moduleResolution": "NodeNext"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`"module": "ESNext"` with `"moduleResolution": "bundler"` also works if you use a bundler, but for direct Node execution, `NodeNext` is correct.
|
|
55
|
+
|
|
56
|
+
### 4. Importing CommonJS packages from ESM
|
|
57
|
+
|
|
58
|
+
Some packages only export CommonJS. Use the default import:
|
|
59
|
+
|
|
60
|
+
```javascript
|
|
61
|
+
// If named imports fail:
|
|
62
|
+
import pkg from 'some-cjs-package';
|
|
63
|
+
const { namedExport } = pkg;
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Quick Checklist
|
|
67
|
+
|
|
68
|
+
- `package.json` has `"type": "module"`
|
|
69
|
+
- All relative imports include `.js` extension
|
|
70
|
+
- `tsconfig.json` uses `NodeNext` module resolution
|
|
71
|
+
- CJS dependencies use default import pattern
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: npm-peer-deps
|
|
3
|
+
title: "npm peer dependency conflicts"
|
|
4
|
+
language: javascript
|
|
5
|
+
error_types: ["ERESOLVE", "peer dep", "Could not resolve dependency"]
|
|
6
|
+
tags: [debugging, npm, dependencies, node]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Problem
|
|
10
|
+
|
|
11
|
+
`npm install` fails with `ERESOLVE could not resolve dependency` because two packages require incompatible versions of the same peer dependency.
|
|
12
|
+
|
|
13
|
+
## Understand the Conflict
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm ls <conflicting-package>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
This shows the dependency tree and which packages require which versions.
|
|
20
|
+
|
|
21
|
+
## Quick Fixes
|
|
22
|
+
|
|
23
|
+
### 1. `--legacy-peer-deps` (safest workaround)
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install --legacy-peer-deps
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Tells npm to ignore peer dependency conflicts entirely, using the npm v6 behavior. Does not force install — just skips the strict resolution.
|
|
30
|
+
|
|
31
|
+
### 2. `--force` (more aggressive)
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install --force
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Forces installation even with conflicts. Can produce a broken `node_modules` if versions are truly incompatible at runtime.
|
|
38
|
+
|
|
39
|
+
### 3. Make it persistent
|
|
40
|
+
|
|
41
|
+
Add to `.npmrc` so the whole team gets the same behavior:
|
|
42
|
+
|
|
43
|
+
```ini
|
|
44
|
+
legacy-peer-deps=true
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Proper Fixes
|
|
48
|
+
|
|
49
|
+
### 1. Update packages to compatible versions
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm outdated
|
|
53
|
+
npm update <package>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Often the conflict goes away when you update to the latest versions of both packages.
|
|
57
|
+
|
|
58
|
+
### 2. Use `overrides` (npm 8.3+)
|
|
59
|
+
|
|
60
|
+
Force a specific version of the conflicting dependency in `package.json`:
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"overrides": {
|
|
65
|
+
"react": "^18.0.0"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
This tells npm to use React 18 everywhere, even if some packages declare a peer dep on React 17.
|
|
71
|
+
|
|
72
|
+
### 3. Use `resolutions` (yarn)
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"resolutions": {
|
|
77
|
+
"react": "^18.0.0"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## When to Fix vs Force
|
|
83
|
+
|
|
84
|
+
- **Fix** when the conflicting packages have updates that resolve the issue
|
|
85
|
+
- **Force** when a package declares an overly strict peer dep but actually works with your version (common with React)
|
|
86
|
+
- **Override** when you have tested that the shared dependency works and want a permanent solution
|
|
87
|
+
|
|
88
|
+
### Quick Checklist
|
|
89
|
+
|
|
90
|
+
- `npm ls <package>` to see the conflict tree
|
|
91
|
+
- Try `--legacy-peer-deps` first
|
|
92
|
+
- Check if updating packages resolves the conflict
|
|
93
|
+
- Use `overrides` in package.json for a permanent fix
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: python-import-circular
|
|
3
|
+
title: "Python circular import resolution"
|
|
4
|
+
language: python
|
|
5
|
+
error_types: [ImportError, "cannot import name", "partially initialized module"]
|
|
6
|
+
tags: [debugging, python, imports, circular]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Problem
|
|
10
|
+
|
|
11
|
+
Python raises `ImportError: cannot import name 'X' from partially initialized module` or similar errors caused by two modules importing each other.
|
|
12
|
+
|
|
13
|
+
## Why It Happens
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
# module_a.py
|
|
17
|
+
from module_b import B
|
|
18
|
+
|
|
19
|
+
class A:
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
# module_b.py
|
|
23
|
+
from module_a import A # Fails! module_a is still loading
|
|
24
|
+
|
|
25
|
+
class B:
|
|
26
|
+
pass
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
When Python imports `module_a`, it starts executing it. The first line imports `module_b`, which tries to import `A` from `module_a` — but `module_a` has not finished executing yet, so `A` does not exist.
|
|
30
|
+
|
|
31
|
+
## Fixes
|
|
32
|
+
|
|
33
|
+
### 1. Move imports inside functions (lazy import)
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
# module_b.py
|
|
37
|
+
class B:
|
|
38
|
+
def use_a(self):
|
|
39
|
+
from module_a import A # Import at usage time, not module load
|
|
40
|
+
return A()
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
This breaks the cycle because the import only runs when the function is called, by which time both modules are fully loaded.
|
|
44
|
+
|
|
45
|
+
### 2. Use `TYPE_CHECKING` guard for type annotations
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from __future__ import annotations
|
|
49
|
+
from typing import TYPE_CHECKING
|
|
50
|
+
|
|
51
|
+
if TYPE_CHECKING:
|
|
52
|
+
from module_a import A # Only imported by type checkers, not at runtime
|
|
53
|
+
|
|
54
|
+
class B:
|
|
55
|
+
def use_a(self) -> 'A':
|
|
56
|
+
...
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`TYPE_CHECKING` is `False` at runtime, so the import never executes. The `from __future__ import annotations` makes all annotations strings by default, avoiding runtime evaluation.
|
|
60
|
+
|
|
61
|
+
### 3. Extract shared types into a separate module
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
# types.py (no circular imports — only defines types)
|
|
65
|
+
class ABase:
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
class BBase:
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
# module_a.py
|
|
72
|
+
from types import ABase, BBase
|
|
73
|
+
|
|
74
|
+
# module_b.py
|
|
75
|
+
from types import ABase, BBase
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Moving shared interfaces or base classes to a third module breaks the cycle.
|
|
79
|
+
|
|
80
|
+
### 4. Use `importlib` as an escape hatch
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
import importlib
|
|
84
|
+
|
|
85
|
+
def get_a():
|
|
86
|
+
module_a = importlib.import_module('module_a')
|
|
87
|
+
return module_a.A
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
This is the most flexible but least readable option. Use only when the other approaches do not fit.
|
|
91
|
+
|
|
92
|
+
### 5. Restructure the modules
|
|
93
|
+
|
|
94
|
+
Often circular imports indicate that two modules are too tightly coupled. Consider:
|
|
95
|
+
|
|
96
|
+
- Merging them into a single module
|
|
97
|
+
- Extracting the shared dependency into a third module
|
|
98
|
+
- Using dependency injection instead of direct imports
|
|
99
|
+
|
|
100
|
+
## Detection
|
|
101
|
+
|
|
102
|
+
Circular imports typically show up as:
|
|
103
|
+
- `ImportError: cannot import name 'X'`
|
|
104
|
+
- `AttributeError: partially initialized module`
|
|
105
|
+
- The error only appears when importing in a specific order
|
|
106
|
+
|
|
107
|
+
### Quick Checklist
|
|
108
|
+
|
|
109
|
+
- Move imports inside functions for quick fixes
|
|
110
|
+
- Use `TYPE_CHECKING` guard for type-only imports
|
|
111
|
+
- Extract shared types into a separate module
|
|
112
|
+
- Consider restructuring if circles are deep
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: python-import-resolution
|
|
3
|
+
title: "Python import resolution"
|
|
4
|
+
language: python
|
|
5
|
+
error_types: [ImportError, ModuleNotFoundError]
|
|
6
|
+
tags: [debugging, imports, python, packages]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Problem
|
|
10
|
+
|
|
11
|
+
Python raises `ImportError` or `ModuleNotFoundError` when importing modules, even though the file exists on disk.
|
|
12
|
+
|
|
13
|
+
## Root Causes & Fixes
|
|
14
|
+
|
|
15
|
+
### 1. Module not on `sys.path`
|
|
16
|
+
|
|
17
|
+
Python only searches directories listed in `sys.path`. Check it:
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
import sys
|
|
21
|
+
print(sys.path)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Common fix — run as a module from the project root:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
python -m mypackage.mymodule
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
This adds the current directory to `sys.path` automatically.
|
|
31
|
+
|
|
32
|
+
### 2. Missing `__init__.py`
|
|
33
|
+
|
|
34
|
+
For Python to treat a directory as a package, it needs an `__init__.py` file (can be empty):
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
myproject/
|
|
38
|
+
mypackage/
|
|
39
|
+
__init__.py
|
|
40
|
+
module_a.py
|
|
41
|
+
module_b.py
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 3. Relative vs absolute imports
|
|
45
|
+
|
|
46
|
+
Inside a package, use relative imports:
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
# Inside mypackage/module_b.py
|
|
50
|
+
from .module_a import some_function # relative
|
|
51
|
+
from mypackage.module_a import some_function # absolute
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Relative imports only work when running as a package (`python -m`), not as a script (`python module_b.py`).
|
|
55
|
+
|
|
56
|
+
### 4. Local package not installed
|
|
57
|
+
|
|
58
|
+
For a local package to be importable from anywhere, install it in development mode:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install -e .
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
This requires a `pyproject.toml` or `setup.py` at the project root.
|
|
65
|
+
|
|
66
|
+
### 5. Wrong Python interpreter
|
|
67
|
+
|
|
68
|
+
Verify you are using the expected Python:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
which python
|
|
72
|
+
python --version
|
|
73
|
+
pip list | grep mypackage
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Virtual environments frequently cause confusion — make sure the venv is activated before running.
|
|
77
|
+
|
|
78
|
+
### Quick Checklist
|
|
79
|
+
|
|
80
|
+
- `__init__.py` exists in every package directory
|
|
81
|
+
- Running with `python -m` from project root
|
|
82
|
+
- Package installed with `pip install -e .` for development
|
|
83
|
+
- Correct Python interpreter is active (`which python`)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: python-venv-activate
|
|
3
|
+
title: "Python virtual environment activation"
|
|
4
|
+
language: python
|
|
5
|
+
error_types: ["ModuleNotFoundError", "command not found: python", "No module named pip"]
|
|
6
|
+
tags: [debugging, python, venv, environment]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Problem
|
|
10
|
+
|
|
11
|
+
Python virtual environment is not activating, or the wrong Python/pip is being used after activation. Packages install globally instead of in the venv.
|
|
12
|
+
|
|
13
|
+
## Activation Commands
|
|
14
|
+
|
|
15
|
+
### Linux / macOS
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
source venv/bin/activate
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Windows (PowerShell)
|
|
22
|
+
|
|
23
|
+
```powershell
|
|
24
|
+
.\venv\Scripts\Activate.ps1
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Windows (cmd)
|
|
28
|
+
|
|
29
|
+
```cmd
|
|
30
|
+
venv\Scripts\activate.bat
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
After activation your prompt should show `(venv)` prefix.
|
|
34
|
+
|
|
35
|
+
## Verify Correct Python
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
which python # Linux/macOS
|
|
39
|
+
where python # Windows
|
|
40
|
+
python --version
|
|
41
|
+
pip list # Should show only venv packages
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
If `which python` does not point to `venv/bin/python`, the venv is not active.
|
|
45
|
+
|
|
46
|
+
## Common Issues
|
|
47
|
+
|
|
48
|
+
### 1. Deactivate first if switching venvs
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
deactivate
|
|
52
|
+
source other-venv/bin/activate
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 2. PowerShell execution policy blocks activation
|
|
56
|
+
|
|
57
|
+
```powershell
|
|
58
|
+
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 3. Venv is corrupted or wrong Python version
|
|
62
|
+
|
|
63
|
+
Delete and recreate:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
rm -rf venv
|
|
67
|
+
python3.11 -m venv venv # Use specific Python version
|
|
68
|
+
source venv/bin/activate
|
|
69
|
+
pip install -r requirements.txt
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 4. IDE not using the venv
|
|
73
|
+
|
|
74
|
+
In VS Code, open the Command Palette and select `Python: Select Interpreter`, then choose the interpreter inside your `venv/` directory.
|
|
75
|
+
|
|
76
|
+
### 5. `pip` installs globally
|
|
77
|
+
|
|
78
|
+
Always check that pip belongs to the venv:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
which pip
|
|
82
|
+
# Should be: /path/to/project/venv/bin/pip
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
If not, activate the venv or use:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
python -m pip install <package>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
This always uses the pip associated with the active `python`.
|
|
92
|
+
|
|
93
|
+
### Quick Checklist
|
|
94
|
+
|
|
95
|
+
- `source venv/bin/activate` (Linux/macOS) or `.\venv\Scripts\Activate.ps1` (Windows)
|
|
96
|
+
- `which python` should point to the venv
|
|
97
|
+
- Recreate venv if corrupted: `python -m venv venv`
|
|
98
|
+
- Use `python -m pip` to guarantee correct pip
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: react-stale-closure
|
|
3
|
+
title: "React stale closure in hooks"
|
|
4
|
+
language: javascript
|
|
5
|
+
error_types: ["stale state", "useEffect stale closure", "useState not updating"]
|
|
6
|
+
tags: [debugging, react, hooks, closures]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Problem
|
|
10
|
+
|
|
11
|
+
A `useEffect`, `useCallback`, or event handler reads a state variable but always sees the old value instead of the current one. This is a stale closure — the function captured a snapshot of state at render time.
|
|
12
|
+
|
|
13
|
+
## How It Happens
|
|
14
|
+
|
|
15
|
+
```jsx
|
|
16
|
+
const [count, setCount] = useState(0);
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
const id = setInterval(() => {
|
|
20
|
+
console.log(count); // Always logs 0!
|
|
21
|
+
}, 1000);
|
|
22
|
+
return () => clearInterval(id);
|
|
23
|
+
}, []); // Empty deps = captures initial count forever
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The effect closes over `count` at its initial value. Because the dependency array is empty, it never re-runs to capture the updated value.
|
|
27
|
+
|
|
28
|
+
## Fixes
|
|
29
|
+
|
|
30
|
+
### 1. Add the variable to the dependency array
|
|
31
|
+
|
|
32
|
+
```jsx
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
const id = setInterval(() => {
|
|
35
|
+
console.log(count); // Now current
|
|
36
|
+
}, 1000);
|
|
37
|
+
return () => clearInterval(id);
|
|
38
|
+
}, [count]); // Re-runs when count changes
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 2. Use functional state updates
|
|
42
|
+
|
|
43
|
+
When you need the latest state but don't want to re-run the effect:
|
|
44
|
+
|
|
45
|
+
```jsx
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
const id = setInterval(() => {
|
|
48
|
+
setCount((prev) => prev + 1); // Always has latest value
|
|
49
|
+
}, 1000);
|
|
50
|
+
return () => clearInterval(id);
|
|
51
|
+
}, []); // Safe — no stale closure
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 3. Use a ref for read-only access
|
|
55
|
+
|
|
56
|
+
```jsx
|
|
57
|
+
const countRef = useRef(count);
|
|
58
|
+
countRef.current = count; // Sync on every render
|
|
59
|
+
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
const id = setInterval(() => {
|
|
62
|
+
console.log(countRef.current); // Always current
|
|
63
|
+
}, 1000);
|
|
64
|
+
return () => clearInterval(id);
|
|
65
|
+
}, []);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### ESLint `exhaustive-deps` Rule
|
|
69
|
+
|
|
70
|
+
Enable `react-hooks/exhaustive-deps` in your ESLint config. It warns when a dependency is missing from the array. Most stale closure bugs are caught by this rule.
|
|
71
|
+
|
|
72
|
+
### Quick Checklist
|
|
73
|
+
|
|
74
|
+
- Every variable used inside `useEffect`/`useCallback` is in the dependency array
|
|
75
|
+
- Use functional updates (`setX(prev => ...)`) when you need latest state without adding a dependency
|
|
76
|
+
- Use refs for values that change but should not trigger re-runs
|