Not Running
ESLint runs but no Stricture violations appear
Solutions to common issues when using Stricture. Each issue includes symptoms, cause, and step-by-step solutions.
Not Running
ESLint runs but no Stricture violations appear
Config Issues
Config file not found or invalid
Pattern Problems
Rules not matching expected files
Performance
Linting is very slow with Stricture
ESLint runs successfully but never reports Stricture violations, even when violations exist.
$ npx eslint .✨ Done in 1.23s # No errors, but violations should existCheck if the rule is enabled:
# Check ESLint confignpx eslint --print-config src/index.ts | grep strictureIf you see nothing, the rule isn’t enabled.
Solution: Add rule to ESLint config
import stricture from '@stricture/eslint-plugin';
export default [ { plugins: { '@stricture': stricture }, rules: { '@stricture/enforce-boundaries': 'error' // ← Add this } }];module.exports = { plugins: ['@stricture'], rules: { '@stricture/enforce-boundaries': 'error' // ← Add this }};Solution: Install the plugin
npm install -D @stricture/eslint-plugin
# Verify installationnpm list @stricture/eslint-pluginESLint might not be checking TypeScript files.
Solution: Ensure ESLint processes .ts and .tsx files
# ✅ Correctnpx eslint . --ext .ts,.tsx
# ❌ Wrong (only .js)npx eslint .Or in ESLint config:
export default [ { files: ['**/*.ts', '**/*.tsx'], // ← Specify file patterns // ... }];Check if files are excluded:
# Make sure these don't exclude your source filesnode_modules/dist/# src/ ← Should NOT be here!After fixing, verify the rule runs:
# Should show violationsnpx eslint src/ --rule '@stricture/enforce-boundaries: error'
# Debug modenpx eslint src/ --debug 2>&1 | grep strictureError: No Stricture config found at .stricture/config.json at loadConfig (node_modules/@stricture/eslint-plugin/dist/load-config.js:23)Check config file location:
ls -la .stricture/config.jsonThe config must be in your project root (where package.json is).
Solution: Move config to project root
# ❌ Wrong locationssrc/.stricture/config.jsonpackages/.stricture/config.json
# ✅ Correct location.stricture/config.json # Same level as package.jsonFor monorepos, each package needs its own config:
# Monorepo structurepackages/api/.stricture/config.json # ✅ API package rootpackages/web/.stricture/config.json # ✅ Web package root.stricture/config.json # ❌ Not usedSolution: Create the directory
mkdir -p .strictureecho '{"preset":"@stricture/hexagonal"}' > .stricture/config.jsonSolution: Fix file permissions
# Check permissionsls -la .stricture/config.json
# Fix if neededchmod 644 .stricture/config.jsonOn case-sensitive filesystems (Linux), the folder name must be exactly .stricture:
# ❌ Wrong.Stricture/config.json.STRICTURE/config.json
# ✅ Correct.stricture/config.json# Should show config contentscat .stricture/config.json
# Test config loadsnpx eslint src/index.ts --rule '@stricture/enforce-boundaries: error'Error: Invalid Stricture configuration SyntaxError: Unexpected token } in JSON at position 45Validate JSON syntax:
# Use jq to validatecat .stricture/config.json | jq .
# Or use Nodenode -e "console.log(JSON.parse(require('fs').readFileSync('.stricture/config.json')))"// ❌ Wrong{ "preset": "@stricture/hexagonal", // ← Trailing comma}
// ✅ Correct{ "preset": "@stricture/hexagonal"}// ❌ Wrong - JSON doesn't support comments{ // This is my config "preset": "@stricture/hexagonal"}
// ✅ Correct - No comments{ "preset": "@stricture/hexagonal"}// ❌ Wrong{ preset: "@stricture/hexagonal"}
// ✅ Correct{ "preset": "@stricture/hexagonal"}// ❌ Wrong{ 'preset': '@stricture/hexagonal'}
// ✅ Correct{ "preset": "@stricture/hexagonal"}Use a JSON validator:
# Install jsonlintnpm install -g jsonlint
# Validatejsonlint .stricture/config.jsonOr use an IDE with JSON validation (VS Code, WebStorm).
Error: Preset '@stricture/hexagonal' not found at loadPreset (node_modules/@stricture/eslint-plugin/dist/load-config.js:45)Check if preset is installed:
npm list @stricture/hexagonalSolution: Check available presets
Available presets:
@stricture/hexagonal@stricture/layered@stricture/clean@stricture/modular@stricture/nextjs@stricture/nestjsUpdate config:
{ "preset": "@stricture/layered" // ← Correct name}In monorepos, workspace presets must be built first.
Solution: Build preset package
cd packages/stricture-confignpm run build
cd ../apinpm run lint # Now worksSolution: Install dependencies
# Clean installrm -rf node_modules package-lock.jsonnpm install
# Or with pnpmpnpm install --force# Check preset exportsnode -e "console.log(require('@stricture/hexagonal'))"
# Should show preset objectYou expect violations but none are reported, even though imports violate your rules.
import { Database } from '../infrastructure/database' // Should violate!// But no error appearsAdd debug logging:
# Check which boundaries match your filenpx eslint src/domain/user.ts --debug 2>&1 | grep -i boundaryYour boundary pattern might not match the file path.
Diagnosis:
{ "boundaries": [ { "name": "domain", "pattern": "domain/**", // ← Missing src/ "tags": ["domain"] } ]}Solution: Fix pattern to match actual structure
{ "boundaries": [ { "name": "domain", "pattern": "src/domain/**", // ✅ Include src/ "tags": ["domain"] } ]}Test patterns:
# Install globby for testingnpm install -g globby-cli
# Test patternglobby 'src/domain/**' --cwd .Using folder mode when you need file mode (or vice versa).
// ❌ Wrong - folder mode doesn't match individual files{ "name": "domain", "pattern": "src/domain/**", "mode": "folder" // ← Wrong mode}
// ✅ Correct - file mode matches individual files{ "name": "domain", "pattern": "src/domain/**", "mode": "file"}Rule of thumb: Use "mode": "file" for most cases.
You defined boundaries but no rules.
Solution: Add rules that use those boundaries
{ "boundaries": [ /* ... */ ], "rules": [ { "id": "domain-isolation", "from": { "tag": "domain" }, "to": { "tag": "*" }, "allowed": false, "message": "Domain must be isolated" } ]}Your rule doesn’t match the actual import.
// ❌ Wrong - too specific{ "from": { "pattern": "src/domain/user.ts" }, // Only matches user.ts "to": { "tag": "infrastructure" }}
// ✅ Correct - uses tag{ "from": { "tag": "domain" }, // Matches all domain files "to": { "tag": "infrastructure" }}A more specific rule is overriding your expected rule.
Diagnosis: Check rule specificity
{ "rules": [ // This is more specific (pattern-based, score: 10000+) { "id": "allow-specific", "from": { "pattern": "src/domain/user.ts" }, "to": { "tag": "infrastructure" }, "allowed": true // ← This wins! }, // This is less specific (tag-based, score: 101) { "id": "domain-isolation", "from": { "tag": "domain" }, "to": { "tag": "*" }, "allowed": false // ← This loses } ]}Solution: Remove or adjust the more specific rule.
Create a test file that should violate:
// This import should be caughtimport { Database } from '../infrastructure/database'npx eslint src/domain/test-violation.tsIf no error, your rules need adjustment.
Your glob pattern doesn’t match the files you expect.
// ❌ Wrong - regex syntax in glob{ "pattern": "src/domain/.*\\.ts"}
// ✅ Correct - glob syntax{ "pattern": "src/domain/**/*.ts"}// ❌ Wrong - only matches immediate children{ "pattern": "src/domain/*"}
// ✅ Correct - matches all descendants{ "pattern": "src/domain/**"}// ❌ Wrong - missing .ts files{ "pattern": "src/domain/**/*.tsx" // Only .tsx}
// ✅ Correct - both extensions{ "pattern": "src/domain/**/*.{ts,tsx}"}Patterns are relative to project root (where .stricture/config.json is).
// ❌ Wrong - absolute path{ "pattern": "/Users/me/project/src/domain/**"}
// ✅ Correct - relative to project root{ "pattern": "src/domain/**"}Use globby to test:
npm install -g globby-cli
# Test your patternglobby 'src/{domain,application}/**/*.{ts,tsx}' --cwd .Or in Node:
const { globbySync } = require('globby');const matches = globbySync('src/domain/**', { cwd: process.cwd() });console.log(matches);Stricture doesn’t recognize TypeScript path aliases:
// This import should violate but doesn'timport { User } from '@/domain/user' // Using aliasCheck if tsconfig.json is found:
# Should show paths configurationcat tsconfig.json | grep -A 5 pathsSolution: Ensure tsconfig.json is in the same directory as .stricture/config.json
project/├── .stricture/config.json├── tsconfig.json # ✅ Same level├── src/│ └── tsconfig.json # ❌ Not usedSolution: Verify baseUrl and paths
{ "compilerOptions": { "baseUrl": ".", // ← Must be set "paths": { "@/domain/*": ["src/domain/*"], // ✅ Relative to baseUrl "@/infrastructure/*": ["src/infrastructure/*"] } }}Solution: Specify which tsconfig to use
{ "preset": "@stricture/hexagonal", "tsconfig": "./tsconfig.app.json" // ← Custom tsconfig}Test alias resolution:
import { User } from '@/domain/user'import { Database } from '@/infrastructure/database' // Should violatenpx eslint test-alias.ts# Should show violationESLint is very slow with Stricture enabled.
$ time npx eslint .✨ Done in 45.67s # Very slow!Profile ESLint:
TIMING=1 npx eslint .Look for @stricture/enforce-boundaries in timing output.
Solution: Exclude unnecessary files
# Exclude build artifactsdist/build/.next/
# Exclude dependenciesnode_modules/
# Exclude test fixtures**/__fixtures__/**/__mocks__/Solution: Simplify boundary patterns
// ❌ Slow - many patterns{ "boundaries": [ { "pattern": "src/**/*.ts" }, { "pattern": "src/**/*.tsx" }, { "pattern": "src/**/*.js" }, { "pattern": "src/**/*.jsx" } ]}
// ✅ Fast - combined pattern{ "boundaries": [ { "pattern": "src/**/*.{ts,tsx,js,jsx}" } ]}Solution: Enable ESLint caching
# Enable cachenpx eslint . --cache
# Or in package.json{ "scripts": { "lint": "eslint . --cache --cache-location .eslintcache" }}Add to .gitignore:
.eslintcacheSolution: Use ESLint file patterns
# ❌ Slow - checks everythingnpx eslint .
# ✅ Fast - only source filesnpx eslint src/
# ✅ Faster - specific extensionsnpx eslint 'src/**/*.{ts,tsx}'Solution: Use ESLint parallel processing (ESLint 9+)
# ESLint 9+npx eslint . --max-warnings 0
# Or use tools like turbo/nx for monoreposturbo lint # Parallel across packages--cache).eslintignore)lint scriptStricture reports violations for imports that should be allowed.
# Check which rule is triggerednpx eslint src/problematic-file.ts --format json | jq '.[] | .messages'Solution: Add explicit allow rule
{ "rules": [ { "id": "application-to-domain", "from": { "tag": "application" }, "to": { "tag": "domain" }, "allowed": true // ← Add this } ]}Remember: Stricture is deny-by-default. You must explicitly allow legitimate imports.
A file might be matching the wrong boundary.
Diagnosis:
// Is this "shared" or "domain"?Solution: Be more specific with patterns
{ "boundaries": [ { "name": "domain", "pattern": "src/core/domain/**", // ← More specific "tags": ["domain"] }, { "name": "shared", "pattern": "src/shared/**", "tags": ["shared"] } ]}Test files often need to import anything.
Solution: Exclude test files or create test boundary
{ "boundaries": [ { "name": "domain", "pattern": "src/domain/**", "exclude": ["**/*.test.ts", "**/*.spec.ts"], // ← Exclude tests "tags": ["domain"] } ]}Or create a separate test boundary:
{ "boundaries": [ { "name": "tests", "pattern": "**/*.{test,spec}.{ts,tsx}", "tags": ["tests"] } ], "rules": [ { "id": "tests-can-import-anything", "from": { "tag": "tests" }, "to": { "tag": "*" }, "allowed": true } ]}After fixing, the false positive should disappear:
npx eslint src/problematic-file.ts# Should pass nowViolation errors don’t explain how to fix the issue.
src/domain/user.ts 5:1 error Import not allowed @stricture/enforce-boundariesAdd custom messages to your rules:
{ "rules": [ { "id": "domain-isolation", "from": { "tag": "domain" }, "to": { "tag": "*" }, "allowed": false, "message": "Domain layer must be pure - no external dependencies. Move this logic to the application layer or define a port interface.", "examples": { "bad": [ "import { Database } from '../infrastructure/database'" ], "good": [ "// Define a port in core/ports/", "export interface UserRepository { save(user: User): Promise<void> }", "// Use the port in application layer" ] } } ]}Now errors show helpful guidance:
src/domain/user.ts 5:1 error Domain layer must be pure - no external dependencies. Move this logic to the application layer or define a port interface.
Bad example: import { Database } from '../infrastructure/database'
Good example: // Define a port in core/ports/ export interface UserRepository { save(user: User): Promise<void> }
@stricture/enforce-boundariesStricture reports violations in node_modules/ files.
node_modules/some-library/index.js 10:1 error Import not allowed @stricture/enforce-boundariesExclude node_modules from ESLint:
node_modules/Or in ESLint config:
export default [ { ignores: ['node_modules/**', 'dist/**'] }, // ... rest of config];In a monorepo, package-specific config isn’t found.
Error: No Stricture config found at packages/api/.stricture/config.jsonEach package needs its own config:
# Create config for each packagemkdir -p packages/api/.strictureecho '{"preset":"@stricture/hexagonal"}' > packages/api/.stricture/config.json
mkdir -p packages/web/.strictureecho '{"preset":"@stricture/nextjs"}' > packages/web/.stricture/config.jsonSee the Monorepo Guide for detailed setup.
You have circular imports but Stricture doesn’t report them.
import { Order } from './order'
// order.tsimport { User } from './user' // Circular!Stricture enforces architectural boundaries, not circular dependencies directly. Circular imports are often prevented as a side effect of proper architecture.
Use ESLint’s import/no-cycle rule for explicit circular dependency detection:
npm install -D eslint-plugin-importimport importPlugin from 'eslint-plugin-import';
export default [ { plugins: { import: importPlugin }, rules: { 'import/no-cycle': 'error' // Detect circular deps } }];Run ESLint with debug output:
DEBUG=eslint:* npx eslint src/ 2>&1 | grep strictureEnsure compatible versions:
npm list @stricture/eslint-plugin eslintCompatible versions:
Create a minimal reproduction:
mkdir stricture-debugcd stricture-debugnpm init -ynpm install -D @stricture/eslint-plugin eslint typescript
# Create minimal config and test file# Report issue with this minimal reproductionInclude in your bug report:
npm list @stricture/eslint-plugin eslint.stricture/config.jsonCore Concepts
Understand how Stricture works to debug better. Learn concepts →
Migration Guide
Common issues during migration. Migration tips →
Monorepo Setup
Specific monorepo troubleshooting. Monorepo guide →
Custom Presets
Debug custom preset issues. Preset guide →