This guide covers common issues specific to Directus-powered websites. Issues often relate to API configuration, real-time subscriptions, and database performance.
Common Issues Overview
Directus is an open-source headless CMS that wraps your database with a dynamic API. Common troubleshooting scenarios involve API configuration, permissions, relationships, real-time subscriptions, and frontend integration.
Common Issue Categories
Performance Issues
- API query optimization
- Relationship depth impact
- Real-time subscription overhead
- Image transformation performance
Tracking Issues
- Collection-based tracking gaps
- Real-time event tracking
- User role-based analytics
- Flow webhook reliability
Installation Problems
Directus Instance Setup
Database Connection Failures
Symptoms:
- Directus won't start
- "Connection refused" errors
- "Access denied" for database user
Check database configuration:
# .env file
DB_CLIENT="mysql" # or "postgres", "sqlite3", "mssql", "cockroachdb"
DB_HOST="localhost"
DB_PORT="3306"
DB_DATABASE="directus"
DB_USER="directus_user"
DB_PASSWORD="secure_password"
Test connection:
# MySQL test
mysql -h localhost -u directus_user -p directus
# PostgreSQL test
psql -h localhost -U directus_user -d directus
# Check Directus logs
docker-compose logs directus
# or
pm2 logs directus
Common fixes:
-- Grant proper permissions (MySQL)
GRANT ALL PRIVILEGES ON directus.* TO 'directus_user'@'localhost';
FLUSH PRIVILEGES;
-- PostgreSQL
GRANT ALL PRIVILEGES ON DATABASE directus TO directus_user;
SDK Installation Issues
Install Directus SDK:
# For JavaScript/TypeScript projects
npm install @directus/sdk
# Verify installation
npm list @directus/sdk
TypeScript types:
# Generate types from your schema
npx directus-extension-types export-schema --host http://localhost:8055 --email admin@example.com --password password
Analytics Integration
Tracking Code Not Loading
Implementing analytics in Directus frontend:
// React/Next.js example
import { createDirectus, rest } from '@directus/sdk';
import Script from 'next/script';
function MyApp({ Component, pageProps }) {
return (
<>
<Script
strategy="afterInteractive"
src={`https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX`}
/>
<Script id="google-analytics" strategy="afterInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
`}
</Script>
<Component {...pageProps} />
</>
);
}
Vue/Nuxt example:
// nuxt.config.js
export default {
head: {
script: [
{
src: 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX',
async: true
}
]
},
mounted() {
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
}
}
Configuration Issues
| Issue | Symptoms | Common Causes | Solutions |
|---|---|---|---|
| Permissions Errors | 403 Forbidden on API calls | Role permissions not configured | Update role permissions in Directus admin |
| Deep Relationship Issues | Nested data not loading | Deep query parameter not set | Add ?deep[field]=* to API query |
| Image Not Transforming | Original images served | Transform parameters incorrect | Use proper asset URL format |
| CORS Errors | API blocked in browser | CORS_ORIGIN not configured | Set CORS_ORIGIN in .env |
| Rate Limiting | 429 Too Many Requests | Excessive API calls | Implement caching, increase RATE_LIMITER_POINTS |
| WebSocket Connection Failed | Real-time not working | WebSocket config missing | Configure WEBSOCKETS_ENABLED=true |
| Flow Not Triggering | Automation not running | Flow conditions not met | Check Flow logs in admin |
| File Upload Failing | Upload returns error | Storage adapter misconfigured | Verify STORAGE_LOCATIONS config |
| Auth Token Expired | 401 Unauthorized | Token TTL exceeded | Refresh token or increase AUTH_ACCESS_TOKEN_TTL |
| Collection Not Found | API returns 404 | Collection name mismatch | Verify collection name matches API call |
Debugging with Developer Tools
Directus Admin Debugging
Activity Log Inspection
View API activity:
- Log in to Directus admin
- Navigate to Settings → Activity Log
- Filter by:
- User
- Action (create, update, delete, login)
- Collection
- Time range
Check for:
- Failed authentication attempts
- Permission denials
- Unexpected data changes
Flow Execution Logs
Debug automated workflows:
- Settings → Flows
- Click on specific flow
- View "Logs" tab
- Check execution history:
- Trigger events
- Operation results
- Error messages
// Add logging to Flow operations
{
"operation": "log",
"options": {
"message": "Flow triggered with data: {{$trigger}}"
}
}
API Debugging
SDK Request Logging
Enable request logging:
import { createDirectus, rest, authentication } from '@directus/sdk';
// Custom fetch with logging
const customFetch = (url, options) => {
console.log('API Request:', url);
console.log('Options:', options);
const start = performance.now();
return fetch(url, options).then(response => {
console.log(`Response time: ${performance.now() - start}ms`);
console.log('Status:', response.status);
return response;
});
};
const client = createDirectus('http://localhost:8055', {
globals: {
fetch: customFetch
}
}).with(rest()).with(authentication());
Network Tab Inspection
Monitor Directus API calls:
- Open DevTools → Network tab
- Filter for your Directus domain
- Look for:
/items/collection_name- Collection queries/assets/- File requests/auth/login- Authentication/graphql- GraphQL queries (if enabled)
Check request details:
GET https://your-directus.com/items/articles?fields=*,author.first_name&filter[status][_eq]=published
Headers:
Authorization: Bearer your-token-here
Content-Type: application/json
Response:
Status: 200 OK
{
"data": [...]
}
Browser Console Debugging
Test SDK in console:
// Make client available globally for debugging
window.directusClient = client;
// Test queries in console
const articles = await window.directusClient.request(
readItems('articles', {
fields: ['*'],
limit: 5
})
);
console.log(articles);
Platform-Specific Challenges
Permissions and Role Management
Tracking by User Role
Problem: Need different analytics for different user roles
Solution - Conditional tracking:
import { readMe } from '@directus/sdk';
async function initAnalytics() {
try {
// Get current user
const me = await client.request(readMe({
fields: ['role.name']
}));
const userRole = me.role?.name;
// Don't track admins
if (userRole === 'Administrator') {
console.log('Admin user - analytics disabled');
return;
}
// Initialize analytics
if (typeof gtag !== 'undefined') {
gtag('config', 'G-XXXXXXXXXX', {
'custom_map': {
'dimension1': 'user_role'
}
});
gtag('event', 'page_view', {
'user_role': userRole
});
}
} catch (error) {
console.error('Failed to get user info:', error);
}
}
Public vs Authenticated Content
Track content access level:
async function trackContentView(collectionName, itemId) {
// Check if user is authenticated
const token = await client.getToken();
const isAuthenticated = !!token;
if (typeof gtag !== 'undefined') {
gtag('event', 'content_view', {
'content_type': collectionName,
'content_id': itemId,
'access_level': isAuthenticated ? 'authenticated' : 'public'
});
}
}
Real-Time Subscriptions
Tracking Real-Time Events
Problem: Track when users receive real-time updates
Solution - WebSocket event tracking:
import { createDirectus, realtime } from '@directus/sdk';
const client = createDirectus('http://localhost:8055')
.with(realtime());
// Subscribe to collection
const { subscription, unsubscribe } = await client.subscribe('articles', {
query: {
fields: ['*'],
filter: { status: { _eq: 'published' } }
}
});
// Track subscription events
subscription.on('init', () => {
console.log('Subscription initialized');
if (typeof gtag !== 'undefined') {
gtag('event', 'realtime_connected', {
'collection': 'articles'
});
}
});
subscription.on('message', (data) => {
console.log('Real-time update:', data);
if (typeof gtag !== 'undefined') {
gtag('event', 'realtime_update', {
'collection': 'articles',
'event_type': data.event, // create, update, delete
'item_id': data.data?.id
});
}
});
subscription.on('error', (error) => {
console.error('Subscription error:', error);
if (typeof gtag !== 'undefined') {
gtag('event', 'realtime_error', {
'collection': 'articles',
'error_message': error.message
});
}
});
Collection-Based Tracking
Tracking Different Collections
Problem: Need to track views across multiple collections
Generic tracking function:
async function trackCollectionView(collection, itemId, additionalData = {}) {
try {
// Fetch item details
const item = await client.request(
readItem(collection, itemId, {
fields: ['id', 'title', 'status', 'date_created']
})
);
if (typeof gtag !== 'undefined') {
gtag('event', 'collection_view', {
'collection_name': collection,
'item_id': itemId,
'item_title': item.title,
'item_status': item.status,
...additionalData
});
}
} catch (error) {
console.error('Tracking error:', error);
}
}
// Usage
trackCollectionView('articles', '123');
trackCollectionView('products', '456', { category: 'electronics' });
Flow-Based Analytics
Webhook to Analytics
Problem: Send Directus events to analytics via Flows
Create Flow:
- Trigger: Items Create (articles collection)
- Operation: Webhook
- Method: POST
- URL:
https://your-api.com/track
Webhook payload:
{
"event": "article_created",
"data": {
"id": "{{$trigger.key}}",
"title": "{{$trigger.payload.title}}",
"author": "{{$trigger.accountability.user}}",
"timestamp": "{{$now}}"
}
}
Process in your API:
// pages/api/track.js
export default async function handler(req, res) {
const { event, data } = req.body;
// Send to Google Analytics via Measurement Protocol
await fetch(`https://www.google-analytics.com/mp/collect?measurement_id=G-XXXXXXXXXX&api_secret=YOUR_API_SECRET`, {
method: 'POST',
body: JSON.stringify({
client_id: 'directus-flow',
events: [{
name: event,
params: data
}]
})
});
res.status(200).json({ success: true });
}
Error Messages and Solutions
Common Directus Errors
"Invalid user credentials"
Error:
401 Unauthorized: Invalid user credentials
Causes:
- Wrong email/password
- User account disabled
- 2FA required
Solutions:
// Check authentication
try {
await client.login(email, password);
console.log('Login successful');
} catch (error) {
console.error('Login failed:', error.message);
// Check specific error
if (error.errors?.[0]?.extensions?.code === 'INVALID_CREDENTIALS') {
console.log('Wrong credentials');
} else if (error.errors?.[0]?.extensions?.code === 'USER_SUSPENDED') {
console.log('Account suspended');
}
}
"You don't have permission to access this"
Error:
403 Forbidden: You don't have permission to access this
Check permissions:
- Go to Settings → Roles & Permissions
- Select user's role
- Check collection permissions
- Ensure CRUDS permissions are set correctly
Debug permission:
// Check what user can access
const me = await client.request(
readMe({
fields: ['*', 'role.admin_access', 'role.app_access']
})
);
console.log('User role:', me.role);
"Field doesn't exist"
Error:
Invalid query - Field "field_name" doesn't exist in collection "collection_name"
Solutions:
// List all available fields
const fields = await client.request(
readFields('collection_name')
);
console.log('Available fields:', fields.map(f => f.field));
// Use correct field names
const items = await client.request(
readItems('articles', {
fields: ['id', 'title', 'body'] // Must match exact field names
})
);
API Rate Limiting
Error:
429 Too Many Requests: Rate limit exceeded
Adjust rate limits:
# .env
RATE_LIMITER_ENABLED=true
RATE_LIMITER_POINTS=50 # Requests allowed
RATE_LIMITER_DURATION=1 # Per second
Implement client-side throttling:
import pThrottle from 'p-throttle';
const throttle = pThrottle({
limit: 10, // 10 requests
interval: 1000 // per second
});
const throttledRequest = throttle(async (collection) => {
return await client.request(readItems(collection));
});
// Use throttled function
const articles = await throttledRequest('articles');
Performance Problems
Slow API Queries
Problem: Queries taking too long
Diagnosis:
const start = performance.now();
const data = await client.request(
readItems('articles', {
fields: ['*', 'author.*', 'categories.*']
})
);
console.log(`Query took ${performance.now() - start}ms`);
Optimization strategies:
- Limit fields:
// Bad - fetches everything
const articles = await client.request(readItems('articles'));
// Good - only needed fields
const articles = await client.request(
readItems('articles', {
fields: ['id', 'title', 'slug']
})
);
- Control relationship depth:
// Fetch author but limit fields
const articles = await client.request(
readItems('articles', {
fields: ['*', 'author.first_name', 'author.last_name'],
deep: {
author: {
_filter: {
status: { _eq: 'active' }
}
}
}
})
);
- Add pagination:
const articles = await client.request(
readItems('articles', {
limit: 20,
offset: 0
})
);
- Use search instead of filter for text:
// Better for full-text search
const articles = await client.request(
readItems('articles', {
search: 'keyword',
limit: 10
})
);
Image Transform Performance
Problem: Image transformations slow
Optimize image requests:
// Efficient image URL
const imageUrl = `${directusUrl}/assets/${file.id}?width=800&height=600&fit=cover&quality=80`;
// With Next.js Image component
import Image from 'next/image';
<Image
src={imageUrl}
alt={file.title}
width={800}
height={600}
loading="lazy"
/>
Configure image cache:
# .env
ASSETS_CACHE_TTL="30d"
ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION="6000"
When to Contact Support
Directus Community Support
Contact when:
- Bug in Directus core
- Feature requests
- General usage questions
- Extension development help
Support channels:
- Discord: https://directus.chat
- GitHub Discussions: https://github.com/directus/directus/discussions
- GitHub Issues: For confirmed bugs
- Stack Overflow: Tag
directus
Cloud/Enterprise Support
Directus Cloud:
Self-Hosted Issues:
- Check server logs first
- Database performance tuning
- Infrastructure optimization
When to Hire a Developer
Complex scenarios:
- Custom extension development
- Complex Flow automation
- Performance optimization at scale
- Advanced permission systems
- Multi-tenant implementations
- Custom authentication providers
- Migration from other systems
- Real-time application architecture
Advanced Troubleshooting
Environment Debugging
Complete .env check:
# Core
PORT=8055
PUBLIC_URL="http://localhost:8055"
# Database
DB_CLIENT="mysql"
DB_HOST="localhost"
DB_PORT="3306"
DB_DATABASE="directus"
DB_USER="directus"
DB_PASSWORD="password"
# Auth
KEY="random-key-here"
SECRET="random-secret-here"
# CORS
CORS_ENABLED=true
CORS_ORIGIN=true
# Rate Limiting
RATE_LIMITER_ENABLED=true
RATE_LIMITER_POINTS=50
RATE_LIMITER_DURATION=1
# Caching
CACHE_ENABLED=true
CACHE_STORE="memory"
# File Storage
STORAGE_LOCATIONS="local"
STORAGE_LOCAL_ROOT="./uploads"
# Security
ACCESS_TOKEN_TTL="15m"
REFRESH_TOKEN_TTL="7d"
# WebSockets (for real-time)
WEBSOCKETS_ENABLED=true
Database Query Analysis
Enable query logging:
# .env
DB_LOGGING=true
Check slow queries:
-- MySQL
SELECT * FROM information_schema.processlist
WHERE time > 1
ORDER BY time DESC;
-- PostgreSQL
SELECT pid, now() - query_start as duration, query
FROM pg_stat_activity
WHERE state = 'active'
ORDER BY duration DESC;
Log File Analysis
Check Directus logs:
# Docker
docker logs directus-container
# PM2
pm2 logs directus
# Check log files
tail -f /var/log/directus/error.log
tail -f /var/log/directus/access.log
Enable debug logging:
LOG_LEVEL="debug"
LOG_STYLE="pretty"
Related Global Guides
For platform-agnostic troubleshooting, see: