How to fix SQLITE_PERM: Access permission denied in SQLite

SQLiteINTERMEDIATEMEDIUMUpdated Dec 15, 2025· 341 views

This error occurs when SQLite cannot access a database file or directory due to insufficient file system permissions. Common causes include incorrect ownership, restrictive file modes, or attempting to write to read-only locations.

Quick fix

Fixes SQLITE_PERM

# Check database file permissions
ls -la /path/to/database.db

# Check parent directory permissions
ls -ld /path/to/

# Check auxiliary files if they exist
ls -la /path/to/database.db-*
  1. Check current file and directory permissions
  2. Fix file and directory permissions
  3. Verify application user identity
  4. Fix Docker volume permission issues
sqlite — error
# Check database file permissions
ls -la /path/to/database.db
# Check parent directory permissions
ls -ld /path/to/
# Check auxiliary files if they exist
ls -la /path/to/database.db-*
SQLITE_PERM: Access permission denied

What this error means

SQLITE_PERM is a file system permission error that occurs when SQLite lacks the necessary access rights to perform an operation on a database file or its parent directory. This error code (SQLITE_PERM = 3) indicates that the operating system denied the requested access mode. SQLite requires specific permissions depending on the operation: 1. **Read operations**: Require read permission on the database file 2. **Write operations**: Require write permission on both the database file AND its parent directory 3. **WAL mode**: Requires the ability to create auxiliary files (.wal, .shm) in the same directory 4. **Journal mode**: Requires permission to create temporary journal files The error is particularly common when: - Running applications under restrictive user accounts (like web servers) - Database files are created by one user but accessed by another - Deploying applications to production environments with different permission models - Using containerized applications where volume permissions may not match expectations

Typical symptoms

  • Error message 'SQLITE_PERM: Access permission denied' when opening or writing to database
  • Operations work for some users but fail for others (especially web server accounts)
  • Read operations succeed but write operations fail
  • Database works in development but fails in production/Docker containers
  • Error occurs when trying to create a new database file
  • WAL mode fails even though rollback journal mode works
  • Permission errors specific to auxiliary files like .db-wal or .db-shm

Common causes

  1. Database file or directory has restrictive permissions (e.g., 600 instead of 644)
  2. Database file is owned by a different user than the one running the application
  3. Parent directory lacks write permission, preventing creation of journal/WAL files
  4. Application running as web server user (www-data, nginx, IIS_IUSRS) without proper access
  5. Database file located in a system-protected directory
  6. SELinux or AppArmor policies blocking file access on Linux systems
  7. Windows file system ACLs not granting appropriate permissions
  8. Docker volume mount permissions not matching container user UID/GID
  9. Attempting to write to a read-only file system or network share

How to fix "SQLITE_PERM: Access permission denied"

1Check current file and directory permissions

First, examine the permissions on your database file and its parent directory:

On Linux/macOS:

bash
# Check database file permissions
ls -la /path/to/database.db

# Check parent directory permissions
ls -ld /path/to/

# Check auxiliary files if they exist
ls -la /path/to/database.db-*

On Windows (PowerShell):

powershell
# Check file permissions
Get-Acl "C:\path\to\database.db" | Format-List

# Check directory permissions
Get-Acl "C:\path\to\" | Format-List

Look for:
- File ownership (user:group)
- Read/write/execute permissions
- Whether your application's user has access

2Fix file and directory permissions

Grant appropriate permissions to the database file and directory:

For Linux/macOS (single user):

bash
# Make the database file readable and writable by owner
chmod 644 /path/to/database.db

# Ensure the directory is writable (needed for journal/WAL files)
chmod 755 /path/to/

# If you need group access (e.g., for web server)
chmod 664 /path/to/database.db
chmod 775 /path/to/

For web applications (Linux):

bash
# Change ownership to web server user
sudo chown www-data:www-data /path/to/database.db
sudo chown www-data:www-data /path/to/

# Set appropriate permissions
sudo chmod 660 /path/to/database.db
sudo chmod 770 /path/to/

For Windows (IIS applications):

powershell
# Grant IIS_IUSRS read/write access
$acl = Get-Acl "C:\path\to\database.db"
$permission = "IIS_IUSRS", "FullControl", "Allow"
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule $permission
$acl.SetAccessRule($accessRule)
Set-Acl "C:\path\to\database.db" $acl

# Also grant access to the directory
$acl = Get-Acl "C:\path\to\"
$acl.SetAccessRule($accessRule)
Set-Acl "C:\path\to\" $acl
3Verify application user identity

Determine which user your application is actually running as:

For web applications (Linux):

bash
# Check Apache user
ps aux | grep apache2
# or
ps aux | grep httpd

# Check Nginx user
ps aux | grep nginx

# Check Node.js application user
ps aux | grep node

For containerized applications:

bash
# Check the user inside the container
docker exec <container-name> whoami

# Check the UID/GID
docker exec <container-name> id

For Windows services:

powershell
# Check service account
Get-WmiObject win32_service | Where-Object {$_.Name -eq 'YourServiceName'} | Select Name, StartName

Once you know the user, ensure that user has appropriate permissions on the database file and directory.

4Fix Docker volume permission issues

If running in Docker, ensure volume permissions match the container user:

Option 1: Match container user to host UID/GID

dockerfile
# In your Dockerfile
FROM node:18
RUN useradd -m -u 1000 -s /bin/bash appuser
USER appuser
yaml
# In docker-compose.yml
services:
  app:
    volumes:
      - ./data:/app/data
    user: "1000:1000"  # Match host user UID:GID

Option 2: Fix permissions at runtime

dockerfile
# In your Dockerfile or entrypoint script
RUN chown -R appuser:appuser /app/data

Option 3: Use named volumes with proper initialization

yaml
# In docker-compose.yml
volumes:
  sqlite_data:
    driver: local

services:
  app:
    volumes:
      - sqlite_data:/app/data

Verify permissions inside the container:

bash
docker exec <container-name> ls -la /app/data
docker exec <container-name> touch /app/data/test.txt
5Handle SELinux or AppArmor restrictions

On systems with mandatory access control, additional configuration may be needed:

For SELinux (Red Hat, CentOS, Fedora):

bash
# Check SELinux status
sestatus

# Check context of database file
ls -Z /path/to/database.db

# Allow httpd to write to database directories
sudo setsebool -P httpd_unified 1

# Or set the correct context manually
sudo chcon -t httpd_sys_rw_content_t /path/to/database.db
sudo chcon -t httpd_sys_rw_content_t /path/to/

For AppArmor (Ubuntu, Debian):

bash
# Check AppArmor status
sudo aa-status

# If needed, set profile to complain mode
sudo aa-complain /etc/apparmor.d/usr.sbin.nginx

# Or add exceptions to the profile
sudo nano /etc/apparmor.d/local/usr.sbin.nginx
# Add: /path/to/database.db rw,
sudo systemctl reload apparmor

Temporarily disable to test (not recommended for production):

bash
# SELinux
sudo setenforce 0

# AppArmor
sudo systemctl stop apparmor
6Move database to appropriate location

If the database is in a restricted system directory, move it to a more appropriate location:

For Linux applications:

bash
# Create a data directory with proper permissions
sudo mkdir -p /var/lib/myapp/data
sudo chown www-data:www-data /var/lib/myapp/data
sudo chmod 750 /var/lib/myapp/data

# Move the database
sudo mv /restricted/path/database.db /var/lib/myapp/data/
sudo chown www-data:www-data /var/lib/myapp/data/database.db

For Windows applications:

powershell
# Use application data directory
$dataPath = "$env:ProgramData\MyApp\Data"
New-Item -ItemType Directory -Force -Path $dataPath

# Move and set permissions
Move-Item "C:\Program Files\MyApp\database.db" $dataPath
icacls $dataPath /grant "IIS_IUSRS:(OI)(CI)M"

Recommended locations by platform:
- Linux: /var/lib/myapp/, /opt/myapp/data/, or ~/.local/share/myapp/
- Windows: %ProgramData%\MyApp\, %AppData%\MyApp\
- macOS: ~/Library/Application Support/MyApp/, /usr/local/var/myapp/

Update your application configuration to point to the new location.

7Test permissions systematically

Create a simple test to verify permissions are working:

Test script (test-sqlite-permissions.sh):

bash
#!/bin/bash
DB_PATH="/path/to/database.db"
DB_DIR=$(dirname "$DB_PATH")

echo "Testing SQLite permissions..."
echo ""

# Test directory write
echo "1. Testing directory write permission..."
touch "$DB_DIR/test_file.tmp" 2>/dev/null && rm "$DB_DIR/test_file.tmp" && echo "✓ Directory writable" || echo "✗ Directory NOT writable"

# Test database read
echo "2. Testing database read permission..."
sqlite3 "$DB_PATH" "SELECT 1;" 2>/dev/null && echo "✓ Database readable" || echo "✗ Database NOT readable"

# Test database write
echo "3. Testing database write permission..."
sqlite3 "$DB_PATH" "CREATE TABLE IF NOT EXISTS perm_test (id INTEGER); DROP TABLE IF EXISTS perm_test;" 2>/dev/null && echo "✓ Database writable" || echo "✗ Database NOT writable"

# Check WAL files can be created
echo "4. Testing WAL mode..."
sqlite3 "$DB_PATH" "PRAGMA journal_mode=WAL; PRAGMA journal_mode;" 2>/dev/null && echo "✓ WAL mode works" || echo "✗ WAL mode FAILED"

echo ""
echo "Current permissions:"
ls -la "$DB_PATH"
ls -ld "$DB_DIR"

Run as the application user:

bash
# Run as specific user
sudo -u www-data bash test-sqlite-permissions.sh

# Or in Docker
docker exec -u www-data <container-name> /path/to/test-sqlite-permissions.sh

Advanced notes

### Understanding SQLite Permission Requirements

SQLite's permission requirements are more complex than typical file access because of its transaction mechanisms:

Why directory write permission is required:
When SQLite performs a write transaction, it creates auxiliary files:
- Rollback journal mode: Creates a .db-journal file
- WAL mode: Creates .db-wal and .db-shm files

These files must be in the same directory as the database file, which is why directory write permission is essential even for database writes.

Permission matrix:

| Operation | Database File | Parent Directory | Notes |
|-----------|---------------|------------------|-------|
| Read-only SELECT | Read | - | No directory access needed |
| Write (rollback) | Read + Write | Write | Journal file creation |
| Write (WAL mode) | Read + Write | Write | WAL/SHM file creation |
| CREATE DATABASE | - | Write | New file creation |

### Security Considerations

Minimum privilege principle:

bash
# Database file: Owner can read/write, group can read, others nothing
chmod 640 database.db

# Directory: Owner can list/enter/write, group can list/enter, others nothing
chmod 750 /path/to/data/

Avoid these insecure patterns:
- chmod 777 - Never make database world-writable
- Storing databases in web-accessible directories
- Running applications as root just to fix permissions
- Using chown -R on system directories

Use dedicated database directories:
Create a specific directory for SQLite databases with appropriate ownership and permissions, separate from application code and web content.

### Platform-Specific Gotchas

Linux systemd services:
Services run with DynamicUser=true may have unexpected user IDs. Check with:

bash
systemctl show -p User,UID myservice.service

Windows WSL:
File permissions on Windows drives (/mnt/c/) may not behave as expected. Store databases on the Linux file system (/home/) for proper permission support.

macOS code signing:
Signed applications may have restricted access to certain directories. Use appropriate sandboxing entitlements or store databases in user-accessible locations.

### Debugging Permission Errors

Enable SQLite error logging:

c
// In C/C++ applications
int sqlite3_config(SQLITE_CONFIG_LOG, errorLogCallback, NULL);

Use strace/dtrace to see system calls:

bash
# Linux
strace -e trace=open,openat,access sqlite3 database.db 2>&1 | grep EACCES

# macOS
sudo dtruss -t open sqlite3 database.db 2>&1 | grep "Err#13"

Check audit logs:

bash
# SELinux denials
sudo ausearch -m avc -ts recent

# AppArmor denials
sudo journalctl | grep DENIED

### WAL Mode and Permissions

WAL mode has additional permission implications:
1. Shared memory file (.db-shm) requires reader and writer coordination
2. Multiple processes must all have access to create/read/write WAL files
3. Permission errors in WAL mode may appear as SQLITE_BUSY instead of SQLITE_PERM

If permission issues persist in WAL mode, try:

sql
PRAGMA journal_mode=DELETE; -- Revert to rollback journal

### Docker Best Practices

Create a dedicated user in your Dockerfile:

dockerfile
FROM alpine:latest

# Create app user with specific UID/GID
RUN addgroup -g 1000 appgroup && \
    adduser -D -u 1000 -G appgroup appuser

# Create data directory with correct ownership
RUN mkdir -p /app/data && \
    chown -R appuser:appgroup /app/data

USER appuser
WORKDIR /app

For existing containers, fix permissions in entrypoint:

bash
#!/bin/sh
# entrypoint.sh
chown -R $(id -u):$(id -g) /app/data
exec "$@"

Frequently asked questions

What does "SQLITE_PERM" mean?
SQLITE_PERM is a file system permission error that occurs when SQLite lacks the necessary access rights to perform an operation on a database file or its parent directory. This error code (SQLITE_PERM = 3) indicates that the operating system denied the requested access mode. SQLite requires specific permissions depending on the operation: 1.
How do I fix "SQLITE_PERM"?
This error occurs when SQLite cannot access a database file or directory due to insufficient file system permissions. Common causes include incorrect ownership, restrictive file modes, or attempting to write to read-only locations.
What causes "SQLITE_PERM"?
Database file or directory has restrictive permissions (e.g., 600 instead of 644) Database file is owned by a different user than the one running the application Parent directory lacks write permission, preventing creation of journal/WAL files
Is the "SQLITE_PERM" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading