16 KiB
Mission Deletion N8N IDs Issue - Complete Analysis
🔍 Problem Statement
When deleting a mission, the N8N deletion workflow is not working because the database does not contain the integration IDs (Leantime, Outline, Gitea, RocketChat). This prevents N8N from properly cleaning up external integrations.
🔄 Current Flow Analysis
Mission Creation Flow
1. Frontend → POST /api/missions
↓
2. Backend creates mission in Prisma
✅ Mission created with NULL integration IDs:
- leantimeProjectId: null
- outlineCollectionId: null
- giteaRepositoryUrl: null
- rocketChatChannelId: null
↓
3. Backend uploads files to Minio
↓
4. Backend triggers N8N workflow (async)
✅ Sends missionId to N8N
↓
5. N8N creates external integrations:
- Gitea repository
- Leantime project
- Outline collection
- RocketChat channel
↓
6. N8N should call → POST /api/missions/mission-created
⚠️ PROBLEM: This callback may fail or not be called
↓
7. Backend should save IDs to database
❌ If step 6 fails, IDs are never saved
Mission Deletion Flow
1. Frontend → DELETE /api/missions/[missionId]
↓
2. Backend reads mission from database
❌ Integration IDs are NULL (if step 6 above failed)
↓
3. Backend prepares deletion data for N8N:
{
repoName: "", // Empty because giteaRepositoryUrl is null
leantimeProjectId: 0, // 0 because leantimeProjectId is null
documentationCollectionId: "", // Empty because outlineCollectionId is null
rocketchatChannelId: "" // Empty because rocketChatChannelId is null
}
↓
4. Backend sends to N8N deletion workflow
❌ N8N receives empty IDs and cannot delete integrations
↓
5. N8N fails to clean up external resources
📋 Code Analysis
1. Mission Creation - Saving IDs
File: app/api/missions/route.ts
Lines 260-262: Mission is created WITHOUT integration IDs
const mission = await prisma.mission.create({
data: missionData // No integration IDs here
});
Lines 413-423: N8N is triggered with missionId
const n8nData = {
...body,
missionId: mission.id, // ✅ missionId is sent to N8N
creatorId: userId,
logoPath: logoPath,
logoUrl: logoUrl,
config: {
N8N_API_KEY: process.env.N8N_API_KEY,
MISSION_API_URL: process.env.NEXT_PUBLIC_API_URL
}
};
const workflowResult = await n8nService.triggerMissionCreation(n8nData);
Issue: The API returns success immediately without waiting for N8N to save the IDs.
2. N8N Callback Endpoint
File: app/api/missions/mission-created/route.ts
Lines 64-69: Endpoint prefers missionId over name + creatorId
if (body.missionId) {
// ✅ Use missionId if provided (more reliable)
logger.debug('Looking up mission by ID', { missionId: body.missionId });
mission = await prisma.mission.findUnique({
where: { id: body.missionId }
});
}
Lines 128-150: Maps N8N fields to Prisma fields
// Mapper les champs N8N vers notre schéma Prisma
if (body.gitRepoUrl !== undefined) {
updateData.giteaRepositoryUrl = body.gitRepoUrl || null;
}
if (body.leantimeProjectId !== undefined) {
updateData.leantimeProjectId = body.leantimeProjectId
? String(body.leantimeProjectId)
: null;
}
if (body.documentationCollectionId !== undefined) {
updateData.outlineCollectionId = body.documentationCollectionId || null;
}
if (body.rocketchatChannelId !== undefined) {
updateData.rocketChatChannelId = body.rocketchatChannelId || null;
}
Status: ✅ Endpoint exists and should work correctly
3. Mission Deletion - Reading IDs
File: app/api/missions/[missionId]/route.ts
Lines 302-311: Mission is fetched from database
const mission = await prisma.mission.findUnique({
where: { id: params.missionId },
include: {
missionUsers: {
include: {
user: true
}
}
}
});
Lines 356-372: Deletion data is prepared
const n8nDeletionData = {
missionId: mission.id,
name: mission.name,
repoName: repoName, // Extracted from giteaRepositoryUrl (may be empty)
leantimeProjectId: mission.leantimeProjectId || 0, // ❌ 0 if null
documentationCollectionId: mission.outlineCollectionId || '', // ❌ Empty if null
rocketchatChannelId: mission.rocketChatChannelId || '', // ❌ Empty if null
// ...
};
Problem: If IDs are null in database, N8N receives empty values.
🔍 Root Cause Analysis
Possible Causes
-
N8N Workflow Not Calling
/mission-created- N8N workflow might not be configured to call the callback endpoint
- The "Save Mission To API" node might be missing or misconfigured
- Network issues preventing the callback
-
N8N Callback Failing
- API key mismatch
- Mission lookup failing (name/creatorId mismatch)
- Network timeout
- Server error
-
Timing Issues
- N8N workflow takes time to complete
- User deletes mission before N8N saves IDs
- Race condition
-
N8N Not Sending
missionId- N8N might only send
name + creatorId - If mission name is not unique, lookup fails
- N8N might only send
✅ Verification Steps
Step 1: Check if IDs are being saved
Query the database:
SELECT
id,
name,
giteaRepositoryUrl,
leantimeProjectId,
outlineCollectionId,
rocketChatChannelId,
createdAt
FROM "Mission"
WHERE createdAt > NOW() - INTERVAL '7 days'
ORDER BY createdAt DESC;
Expected: Recent missions should have integration IDs populated.
If NULL: N8N callback is not working.
Step 2: Check N8N Workflow Configuration
Verify N8N workflow has "Save Mission To API" node:
- Node should POST to:
{{ MISSION_API_URL }}/mission-created - Should include
missionIdin body - Should include
x-api-keyheader - Should include integration IDs in body
Expected format:
{
"missionId": "uuid-here",
"name": "Mission Name",
"creatorId": "user-id",
"gitRepoUrl": "https://gite.slm-lab.net/alma/repo-name",
"leantimeProjectId": "123",
"documentationCollectionId": "collection-id",
"rocketchatChannelId": "channel-id"
}
Step 3: Check Server Logs
Look for:
Mission Created Webhook Received
Received mission-created data: { ... }
Found mission: { id: "...", name: "..." }
Updating giteaRepositoryUrl: ...
Mission updated successfully
If missing: N8N is not calling the endpoint.
Step 4: Test N8N Callback Manually
Send test request:
curl -X POST https://hub.slm-lab.net/api/missions/mission-created \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_N8N_API_KEY" \
-d '{
"missionId": "existing-mission-id",
"gitRepoUrl": "https://gite.slm-lab.net/alma/test-repo",
"leantimeProjectId": "999",
"documentationCollectionId": "test-collection",
"rocketchatChannelId": "test-channel"
}'
Expected: 200 OK with updated mission data.
🔧 Solutions
Solution 1: Verify N8N Workflow Configuration (IMMEDIATE)
Check N8N workflow "Save Mission To API" node:
-
URL should be:
{{ $node['Process Mission Data'].json.config.MISSION_API_URL }}/mission-createdOr hardcoded:
https://hub.slm-lab.net/api/missions/mission-created -
Headers should include:
Content-Type: application/json x-api-key: {{ $node['Process Mission Data'].json.config.N8N_API_KEY }} -
Body should include:
- ✅
missionId(from original request) - ✅
gitRepoUrl(from Git repository creation) - ✅
leantimeProjectId(from Leantime project creation) - ✅
documentationCollectionId(from Outline collection creation) - ✅
rocketchatChannelId(from RocketChat channel creation)
- ✅
-
Verify node execution:
- Check N8N execution logs
- Verify node is not set to "continueOnFail"
- Check for errors in node execution
Solution 2: Add Logging to Track Callback (DEBUGGING)
Add more detailed logging in app/api/missions/mission-created/route.ts:
logger.debug('Mission Created Webhook Received', {
headers: {
hasApiKey: !!request.headers.get('x-api-key'),
contentType: request.headers.get('content-type')
}
});
const body = await request.json();
logger.debug('Received mission-created data', {
missionId: body.missionId,
name: body.name,
creatorId: body.creatorId,
hasGitRepoUrl: !!body.gitRepoUrl,
hasLeantimeProjectId: !!body.leantimeProjectId,
hasDocumentationCollectionId: !!body.documentationCollectionId,
hasRocketchatChannelId: !!body.rocketchatChannelId,
fullBody: body // Log full body for debugging
});
Solution 3: Add Fallback Lookup (ROBUSTNESS)
Improve mission lookup in app/api/missions/mission-created/route.ts:
// Try missionId first
if (body.missionId) {
mission = await prisma.mission.findUnique({
where: { id: body.missionId }
});
if (!mission) {
logger.warn('Mission not found by ID, trying name + creatorId', {
missionId: body.missionId
});
}
}
// Fallback to name + creatorId
if (!mission && body.name && body.creatorId) {
mission = await prisma.mission.findFirst({
where: {
name: body.name,
creatorId: body.creatorId
},
orderBy: { createdAt: 'desc' }
});
}
// If still not found, try just by name (last resort)
if (!mission && body.name) {
logger.warn('Mission not found by name + creatorId, trying just name', {
name: body.name,
creatorId: body.creatorId
});
mission = await prisma.mission.findFirst({
where: { name: body.name },
orderBy: { createdAt: 'desc' }
});
}
Solution 4: Add Retry Mechanism (RELIABILITY)
Add retry logic for N8N callback (if N8N supports it):
- Configure N8N to retry failed callbacks
- Or implement a webhook retry queue
Solution 5: Manual ID Update Script (MIGRATION)
Create a script to manually update existing missions:
// scripts/update-mission-ids.ts
import { prisma } from '@/lib/prisma';
async function updateMissionIds() {
// Get missions without IDs
const missions = await prisma.mission.findMany({
where: {
OR: [
{ giteaRepositoryUrl: null },
{ leantimeProjectId: null },
{ outlineCollectionId: null },
{ rocketChatChannelId: null }
]
}
});
for (const mission of missions) {
// Manually update IDs if you know them
// Or query external services to find them
await prisma.mission.update({
where: { id: mission.id },
data: {
giteaRepositoryUrl: '...', // From Gitea
leantimeProjectId: '...', // From Leantime
outlineCollectionId: '...', // From Outline
rocketChatChannelId: '...' // From RocketChat
}
});
}
}
🧪 Testing Plan
Test 1: Create Mission and Verify IDs Saved
- Create a new mission via frontend
- Wait 30-60 seconds for N8N to complete
- Query database to verify IDs are saved:
SELECT * FROM "Mission" WHERE name = 'Test Mission'; - Expected: All integration IDs should be populated
Test 2: Check N8N Execution Logs
- Go to N8N execution history
- Find the latest mission creation execution
- Check "Save Mission To API" node:
- ✅ Node executed successfully
- ✅ Response is 200 OK
- ✅ Body contains integration IDs
Test 3: Test Deletion with IDs
- Delete a mission that has IDs saved
- Check N8N deletion workflow execution
- Expected: N8N should receive non-empty IDs and successfully delete integrations
Test 4: Test Deletion without IDs
- Delete a mission that has NULL IDs
- Check N8N deletion workflow execution
- Expected: N8N receives empty IDs and logs warning (but mission still deleted)
📊 Expected vs Actual Behavior
Expected Behavior
Mission Creation:
- Mission created in database
- N8N workflow triggered
- N8N creates integrations
- N8N calls
/mission-createdwith IDs - IDs saved to database ✅
Mission Deletion:
- Mission fetched from database (with IDs) ✅
- IDs sent to N8N deletion workflow ✅
- N8N deletes integrations ✅
- Mission deleted from database ✅
Actual Behavior (Current Issue)
Mission Creation:
- Mission created in database ✅
- N8N workflow triggered ✅
- N8N creates integrations ✅
- N8N calls
/mission-created❓ (May fail) - IDs saved to database ❌ (If step 4 fails)
Mission Deletion:
- Mission fetched from database (IDs are NULL) ❌
- Empty IDs sent to N8N deletion workflow ❌
- N8N cannot delete integrations ❌
- Mission deleted from database ✅ (But integrations remain)
🎯 Immediate Action Items
-
✅ Verify N8N Workflow Configuration
- Check "Save Mission To API" node exists
- Verify URL, headers, and body format
- Check execution logs for errors
-
✅ Check Server Logs
- Look for
/mission-createdendpoint calls - Check for errors or missing API key
- Verify mission lookup is working
- Look for
-
✅ Test Manually
- Create a test mission
- Wait for N8N to complete
- Check database for IDs
- If missing, manually test the callback endpoint
-
✅ Fix N8N Workflow (if needed)
- Ensure
missionIdis included in callback - Verify all integration IDs are included
- Test the workflow end-to-end
- Ensure
-
✅ Update Existing Missions (if needed)
- Manually update IDs for critical missions
- Or create migration script
📝 Code Changes Needed
No Code Changes Required (if N8N is configured correctly)
The endpoint /api/missions/mission-created already exists and should work. The issue is likely:
- N8N workflow not calling it
- N8N workflow calling it incorrectly
- Network/authentication issues
Optional Improvements
- Better error handling in
/mission-createdendpoint - Retry mechanism for failed callbacks
- Monitoring/alerting when IDs are not saved
- Migration script for existing missions
🔍 Debugging Commands
Check Recent Missions Without IDs
SELECT
id,
name,
createdAt,
CASE WHEN giteaRepositoryUrl IS NULL THEN 'MISSING' ELSE 'OK' END as gitea,
CASE WHEN leantimeProjectId IS NULL THEN 'MISSING' ELSE 'OK' END as leantime,
CASE WHEN outlineCollectionId IS NULL THEN 'MISSING' ELSE 'OK' END as outline,
CASE WHEN rocketChatChannelId IS NULL THEN 'MISSING' ELSE 'OK' END as rocketchat
FROM "Mission"
WHERE createdAt > NOW() - INTERVAL '7 days'
ORDER BY createdAt DESC;
Check Mission with IDs
SELECT
id,
name,
giteaRepositoryUrl,
leantimeProjectId,
outlineCollectionId,
rocketChatChannelId
FROM "Mission"
WHERE id = 'your-mission-id';
Test Callback Endpoint
# Replace with actual values
curl -X POST https://hub.slm-lab.net/api/missions/mission-created \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_N8N_API_KEY" \
-d '{
"missionId": "mission-uuid-here",
"gitRepoUrl": "https://gite.slm-lab.net/alma/test",
"leantimeProjectId": "123",
"documentationCollectionId": "collection-456",
"rocketchatChannelId": "channel-789"
}'
✅ Conclusion
Root Cause: N8N workflow is likely not calling /api/missions/mission-created endpoint, or the callback is failing silently.
Solution:
- Verify N8N workflow configuration
- Check N8N execution logs
- Test callback endpoint manually
- Fix N8N workflow if needed
- Manually update existing missions if necessary
Status: Endpoint exists and should work. Issue is in N8N workflow configuration or execution.
Document Created: $(date) Last Updated: $(date) Priority: HIGH - Blocks proper mission deletion