mdbook-validator 1.2.0

An mdBook preprocessor that validates code blocks using Docker containers
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
# Troubleshooting

This guide covers common errors and platform-specific issues when using mdbook-validator.

## Quick Reference

| Code | Error Type | Quick Fix |
|------|------------|-----------|
| E001 | Configuration | Check book.toml syntax and structure |
| E002 | Container Startup | Ensure Docker is running and image exists |
| E003 | Container Exec | Check command syntax and container health |
| E004 | Setup Failed | Fix SETUP block script errors |
| E005 | Query Failed | Fix visible content (SQL, script) errors |
| E006 | Validation Failed | Fix assertion mismatches or output issues |
| E007 | Unknown Validator | Add validator to book.toml config |
| E008 | Invalid Config | Add required fields (container, script) |
| E009 | Fixtures Error | Check fixtures_dir path exists and is a directory |
| E010 | Script Not Found | Check validator script path is correct |
| E011 | Mutually Exclusive | Remove either `hidden` or `skip` (can't use both) |

---

## Error Reference

### E001: Configuration Error

**Message**: `[E001] Configuration error: {message}`

**Common Causes**:
- Invalid TOML syntax in book.toml
- Missing `[preprocessor.validator]` section
- Typo in configuration key names

**How to Fix**:
1. Validate your TOML syntax:
   ```bash
   # Check for syntax errors
   cat book.toml | python3 -c "import tomllib, sys; tomllib.load(sys.stdin.buffer)"
   ```
2. Ensure the basic structure exists:
   ```toml
   [preprocessor.validator]
   command = "mdbook-validator"
   ```
3. Check for common typos: `validators` not `validator`, `container` not `containers`

**Example**:
```
[E001] Configuration error: expected `=`, found newline
```
Fix: Check for missing `=` in your book.toml key-value pairs.

---

### E002: Container Startup Failed

**Message**: `[E002] Container startup failed: {message}`

**Common Causes**:
- Docker daemon not running
- Container image doesn't exist or can't be pulled
- Network connectivity issues preventing image download
- Invalid image tag format

**How to Fix**:
1. Verify Docker is running:
   ```bash
   docker info
   ```
   If this fails, start Docker Desktop (macOS/Windows) or the Docker daemon (Linux).

2. Test that the image can be pulled:
   ```bash
   docker pull keinos/sqlite3:3.47.2  # Replace with your image
   ```

3. Check your network connection if the pull fails.

4. Verify the image tag is valid (avoid `:latest`, use specific versions).

**Example**:
```
[E002] Container startup failed: image not found: badimage:999
```
Fix: Check the `container` value in your validator config matches an existing Docker image.

---

### E003: Container Exec Failed

**Message**: `[E003] Container exec failed: {message}`

**Common Causes**:
- Command doesn't exist in the container
- Container exited unexpectedly
- Timeout waiting for container response
- Permission denied inside container

**How to Fix**:
1. Test the command manually:
   ```bash
   docker run --rm keinos/sqlite3:3.47.2 sqlite3 --version
   ```

2. Check that the `exec_command` in your config is correct:
   ```toml
   [preprocessor.validator.validators.sqlite]
   container = "keinos/sqlite3:3.47.2"
   script = "validators/validate-sqlite.sh"
   exec_command = "sqlite3 -json /tmp/test.db"  # Verify this command exists
   ```

3. Check container logs if available.

**Example**:
```
[E003] Container exec failed: command not found: osqueryi
```
Fix: Ensure the container image includes the required tool, or fix the command name.

---

### E004: Setup Failed

**Message**: `[E004] Setup script failed (exit {exit_code}): {message}`

**Common Causes**:
- Invalid SQL in SETUP block
- Shell syntax error in SETUP script
- Missing prerequisites (table doesn't exist, file not found)
- Permission denied

**How to Fix**:
1. Test your SETUP content manually:
   ```bash
   # For SQLite
   sqlite3 /tmp/test.db "YOUR SETUP SQL HERE"

   # For bash
   bash -c "YOUR SETUP SCRIPT HERE"
   ```

2. Check for syntax errors in your SETUP block:
   ```markdown
   <!--SETUP
   CREATE TABLE test (id INTEGER);  -- Valid SQL
   -->
   ```

3. Ensure SETUP runs before the visible content depends on it.

**Example**:
```
[E004] Setup script failed (exit 1): Error: near "CREAT": syntax error
```
Fix: Correct the typo (`CREAT` → `CREATE`) in your SETUP block.

---

### E005: Query Failed

**Message**: `[E005] Query execution failed (exit {exit_code}): {message}`

**Common Causes**:
- SQL syntax error in the visible content
- Table/column referenced doesn't exist (SETUP may be missing or failed)
- Invalid command for the validator type
- Empty content after marker stripping

**How to Fix**:
1. Test your query manually:
   ```bash
   # Run setup first, then query
   sqlite3 /tmp/test.db "CREATE TABLE t(id INT)"
   sqlite3 -json /tmp/test.db "SELECT * FROM t"
   ```

2. Ensure SETUP creates required tables/data before the query runs.

3. Check that visible content isn't empty after stripping markers.

**Example**:
```
[E005] Query execution failed (exit 1): Error: no such table: users
```
Fix: Add a SETUP block that creates the `users` table before the query.

---

### E006: Validation Failed

**Message**: `[E006] Validation failed (exit {exit_code}): {message}`

**Common Causes**:
- Assertion doesn't match actual output (e.g., `rows = 5` but query returns 3)
- EXPECT block doesn't match actual JSON output
- Validator script logic rejected the output
- Contains assertion failed (string not found in output)

**How to Fix**:
1. Check your assertions match expected output:
   ```markdown
   <!--ASSERT
   rows = 1        -- Exact row count
   rows >= 1       -- Minimum row count
   contains "alice" -- String must appear in output
   -->
   ```

2. For EXPECT blocks, ensure JSON matches exactly:
   ```markdown
   <!--EXPECT
   [{"id": 1, "name": "alice"}]
   -->
   ```

3. Run the query manually to see actual output:
   ```bash
   sqlite3 -json /tmp/test.db "SELECT * FROM users"
   ```

4. Update assertions to match actual behavior.

**Example**:
```
[E006] Validation failed (exit 1): Assertion failed: rows = 5 (actual: 1)
```
Fix: Change `rows = 5` to `rows = 1` or fix your SETUP to insert 5 rows.

---

### E007: Unknown Validator

**Message**: `[E007] Unknown validator '{name}'`

**Common Causes**:
- Typo in validator name (`validtor=sqlite` instead of `validator=sqlite`)
- Validator not configured in book.toml
- Using a validator name that doesn't exist

**How to Fix**:
1. Check spelling in your markdown:
   ```markdown
   ```sql validator=sqlite   <!-- Correct -->
   ```sql validtor=sqlite    <!-- Typo! -->
   ```

2. Add the validator to book.toml:
   ```toml
   [preprocessor.validator.validators.sqlite]
   container = "keinos/sqlite3:3.47.2"
   script = "validators/validate-sqlite.sh"
   ```

3. Ensure validator names match between markdown and config.

**Example**:
```
[E007] Unknown validator 'sqllite'
```
Fix: Correct the typo in your markdown (`sqllite` → `sqlite`).

---

### E008: Invalid Validator Config

**Message**: `[E008] Invalid validator config for '{name}': {reason}`

**Common Causes**:
- Missing `container` field
- Missing `script` field
- Empty values for required fields
- Invalid path format

**How to Fix**:
1. Ensure both required fields are present:
   ```toml
   [preprocessor.validator.validators.myvalidator]
   container = "myimage:1.0"    # Required
   script = "validators/my.sh"  # Required
   exec_command = "..."         # Optional
   ```

2. Don't use empty strings:
   ```toml
   container = ""  # Invalid - must have a value
   ```

3. Check path separators match your OS (use forward slashes).

**Example**:
```
[E008] Invalid validator config for 'sqlite': container cannot be empty
```
Fix: Add a valid container image name to the validator config.

---

### E009: Fixtures Error

**Message**: `[E009] Fixtures directory error: {message}`

**Common Causes**:
- `fixtures_dir` path doesn't exist
- Path exists but is a file, not a directory
- Permission denied accessing the directory
- Relative path resolved incorrectly

**How to Fix**:
1. Check the path exists:
   ```bash
   ls -la fixtures/  # Or your configured path
   ```

2. Ensure it's a directory, not a file:
   ```bash
   test -d fixtures && echo "OK: is a directory"
   ```

3. Use relative paths from book root, or absolute paths:
   ```toml
   [preprocessor.validator]
   fixtures_dir = "fixtures"  # Relative to book.toml location
   ```

**Example**:
```
[E009] Fixtures directory error: 'fixtures' does not exist
```
Fix: Create the directory or update the path in book.toml.

---

### E010: Script Not Found

**Message**: `[E010] Script not found: {path}`

**Common Causes**:
- Validator script file doesn't exist at specified path
- Path typo in book.toml
- Script was deleted or moved
- Wrong relative path base

**How to Fix**:
1. Check the script exists:
   ```bash
   ls -la validators/validate-sqlite.sh
   ```

2. Ensure the script is executable:
   ```bash
   chmod +x validators/validate-sqlite.sh
   ```

3. Verify the path in book.toml is correct (relative to book root):
   ```toml
   [preprocessor.validator.validators.sqlite]
   script = "validators/validate-sqlite.sh"  # Relative to book.toml
   ```

4. Copy validator scripts from the mdbook-validator package if missing.

**Example**:
```
[E010] Script not found: validaters/validate-sqlite.sh
```
Fix: Correct the typo in the path (`validaters` → `validators`).

---

### E011: Mutually Exclusive Attributes

**Message**: `[E011] 'hidden' and 'skip' are mutually exclusive`

**Common Causes**:
- Code block has both `hidden` and `skip` attributes
- Copy-paste error from another block
- Confusion about what each attribute does

**How to Fix**:
1. Understand the difference:
   - `skip` = Don't validate this block, but show it to readers
   - `hidden` = Validate this block, but don't show it to readers

2. Choose one based on your intent:
   ```markdown
   <!-- If you want to show an intentionally broken example (no validation): -->
   ```sql validator=sqlite skip
   SELECT * FROM nonexistent_table;
   ```

   <!-- If you want to validate but hide from readers: -->
   ```sql validator=sqlite hidden
   INSERT INTO users VALUES (1, 'alice');
   ```
   ```

3. Remove the attribute you don't need.

**Example**:
```
[E011] 'hidden' and 'skip' are mutually exclusive
```
Fix: Remove either `hidden` or `skip` from your code block attributes.

---

## Platform-Specific Issues

### macOS

**Docker Desktop Required**:
- Install Docker Desktop from https://docker.com
- Ensure Docker Desktop is running (check menu bar icon)
- Grant necessary permissions when prompted

**Installing jq**:
```bash
brew install jq
```

**Common Issues**:
- "Cannot connect to Docker daemon": Start Docker Desktop application
- Slow first runs: Docker is pulling images (10-30 seconds per validator type)

---

### Linux

**Docker Daemon**:
```bash
# Start Docker daemon
sudo systemctl start docker

# Enable Docker on boot
sudo systemctl enable docker

# Run Docker without sudo (optional)
sudo usermod -aG docker $USER
# Log out and back in for this to take effect
```

**Installing jq**:
```bash
# Debian/Ubuntu
sudo apt-get install jq

# Fedora
sudo dnf install jq

# Arch
sudo pacman -S jq
```

**Common Issues**:
- "Permission denied": Add user to docker group or use sudo
- "Docker daemon not running": Run `systemctl start docker`

---

### Windows

**Docker Desktop Required**:
- Install Docker Desktop from https://docker.com
- Enable WSL 2 backend (recommended) or Hyper-V
- Ensure Docker Desktop is running

**Installing jq**:
```powershell
# Using Chocolatey
choco install jq

# Using Scoop
scoop install jq
```

**Known Limitation - osqueryi stdin bug**:
osqueryi has a known issue (#7972) where stdin piping fails on Windows with "incomplete SQL" errors. Workaround: Use WSL 2 or run validation on Linux/macOS.

---

## Performance Tips

### Container Startup Time

First validation for each validator type takes 10-20 seconds while the container starts. Subsequent validations in the same build reuse the running container.

**Tip**: Group your validated code blocks by validator type to minimize container restarts.

### Image Caching

Docker caches images locally. The first build pulls images (slow), subsequent builds reuse cached images (fast).

**Pre-pull images** before building:
```bash
docker pull keinos/sqlite3:3.47.2
docker pull osquery/osquery:5.17.0-ubuntu22.04
docker pull koalaman/shellcheck-alpine:stable
```

### Large Books

For books with many validated code blocks:
1. Use `fail-fast = true` (default) to stop on first error during development
2. Set `fail-fast = false` in CI to see all errors at once
3. Consider splitting very large books into multiple builds

### Memory Usage

Large JSON outputs are loaded into memory. For queries returning >100MB of data:
- Add `LIMIT` clauses to SQL queries
- Split into multiple smaller queries
- Consider if the full output is necessary for validation

---

## Getting Help

If you encounter an error not covered here:

1. Check the error code (E001-E011) for category
2. Run with `RUST_LOG=debug mdbook build` for verbose output
3. Open an issue at https://github.com/withzombies/mdbook-validator/issues

Include in your report:
- Error message (full text)
- Relevant book.toml configuration
- Code block that caused the error
- OS and Docker version