Loading...
Loading...
Debug and diagnose model errors in Pollinations services. Analyze logs, find error patterns, identify affected users. For taking action on user tiers, see tier-management skill.
npx skill4agent add pollinations/pollinations model-debuggingtier-managementopenai-audiomodalitiessk_User Request → enter.pollinations.ai (Cloudflare Worker)
↓
Logs to Cloudflare Workers Observability
↓
Events stored in D1 database
↓
Batched to Tinybird (async, 100-500 events)
↓
Model Monitor queries Tinybird (model_health.pipe)requestIdx-request-idstatusbodymethodroutePathuserAgentipAddress# Via enter.pollinations.ai worker (requires wrangler)
cd enter.pollinations.ai
npx wrangler d1 execute pollinations-db --remote --command "SELECT model_requested, response_status, error_message, COUNT(*) as count FROM event WHERE response_status >= 400 AND created_at > datetime('now', '-1 hour') GROUP BY model_requested, response_status, error_message ORDER BY count DESC LIMIT 20"cd enter.pollinations.ai
wrangler tail --format json | tee logs.jsonl
# Or with formatting:
wrangler tail --format json | npx tsx scripts/format-logs.ts# Real-time logs
ssh enter-services "sudo journalctl -u image-pollinations.service -f"
# Last 3 minutes
ssh enter-services "sudo journalctl -u image-pollinations.service --since '3 minutes ago' --no-pager" > image-service-logs.txt
# Recent errors only
ssh enter-services "sudo journalctl -u image-pollinations.service -p err -n 50"# Real-time logs
ssh enter-services "sudo journalctl -u text-pollinations.service -f"
# Last 3 minutes
ssh enter-services "sudo journalctl -u text-pollinations.service --since '3 minutes ago' --no-pager" > text-service-logs.txtgetaddrinfo ENOTFOUND gptimagemain1-resource.cognitiveservices.azure.com.envAZURE_CONTENT_SAFETY_ENDPOINT=https://<new-resource>.cognitiveservices.azure.com/
AZURE_CONTENT_SAFETY_API_KEY=<new-key>Content rejected due to sexual/hate/violence content detectionProvided image is not validNo active translate servers availableInvalid value for audio.voiceNo video data in responsessh enter-services "cat /home/ubuntu/pollinations/image.pollinations.ai/.env | grep -E 'AZURE|GOOGLE|CLOUDFLARE'"AZURE_CONTENT_SAFETY_ENDPOINTAZURE_CONTENT_SAFETY_API_KEYGOOGLE_PROJECT_IDAZURE_MYCELI_FLUX_KONTEXT_ENDPOINTssh enter-services "cat /home/ubuntu/pollinations/text.pollinations.ai/.env | grep -E 'AZURE|OPENAI|GOOGLE'"image.pollinations.ai/secrets/env.jsontext.pollinations.ai/secrets/env.json# Decrypt, edit, re-encrypt
sops image.pollinations.ai/secrets/env.json
# Deploy to server
sops --output-type dotenv -d image.pollinations.ai/secrets/env.json > /tmp/image.env
scp /tmp/image.env enter-services:/home/ubuntu/pollinations/image.pollinations.ai/.env
rm /tmp/image.env
# Restart service
ssh enter-services "sudo systemctl restart image-pollinations.service"# Count errors by type
grep -i "error" image-service-logs.txt | grep -oE "(Azure Flux Kontext|Vertex AI|No active translate|getaddrinfo ENOTFOUND)" | sort | uniq -c | sort -rn
# Find content filter rejections
grep -i "Content rejected" image-service-logs.txt | sort | uniq -c
# Check DNS resolution on server
ssh enter-services "nslookup gptimagemain1-resource.cognitiveservices.azure.com"| Model | Backend | Common Issues |
|---|---|---|
| Azure/Replicate | Rate limits, content filter |
| Azure Flux Kontext | Content filter (strict) |
| Vertex AI Gemini | Invalid image URLs, content filter |
| ByteDance ARK | NSFW filter, API key issues |
| Vertex AI | Quota, empty responses |
| Azure OpenAI | Invalid voice names |
| DeepSeek API | Rate limits, API key |
# From wrangler.toml
grep account_id enter.pollinations.ai/wrangler.toml
# Or from existing .env
grep CLOUDFLARE_ACCOUNT_ID image.pollinations.ai/.envWorkers Observability Readenter.pollinations.ai/secrets/env.jsonCLOUDFLARE_OBSERVABILITY_TOKEN# Step 1: Decrypt to temp file
cd /path/to/pollinations
sops -d enter.pollinations.ai/secrets/env.json > /tmp/env.json
# Step 2: Add the token (use jq)
jq '. + {"CLOUDFLARE_OBSERVABILITY_TOKEN": "your_token"}' /tmp/env.json > /tmp/env_updated.json
# Step 3: Re-encrypt (must rename to match .sops.yaml pattern)
cp /tmp/env_updated.json /tmp/env.json
sops -e /tmp/env.json > enter.pollinations.ai/secrets/env.json
# Step 4: Cleanup
rm /tmp/env.json /tmp/env_updated.json
# Verify
sops -d enter.pollinations.ai/secrets/env.json | jq 'keys'.sops.yamlenv.json$POST https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/observability/telemetry/query# Extract credentials from encrypted secrets
ACCOUNT_ID=$(sops -d enter.pollinations.ai/secrets/env.json | jq -r '.CLOUDFLARE_ACCOUNT_ID')
API_TOKEN=$(sops -d enter.pollinations.ai/secrets/env.json | jq -r '.CLOUDFLARE_OBSERVABILITY_TOKEN')curl -s "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/observability/telemetry/keys" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"timeframe": {"from": '$(( $(date +%s) - 86400 ))'000, "to": '$(date +%s)'000}, "datasets": ["workers"]}' | jq '.result[:10]'/queryqueryIdwrangler tail# This format requires a saved query ID
# Query errors with status >= 400
curl -s "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/observability/telemetry/query" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"timeframe": {
"from": '$(( $(date +%s) - 900 ))'000,
"to": '$(date +%s)'000
},
"parameters": {
"datasets": ["workers"],
"filters": [
{"key": "$workers.scriptName", "operation": "eq", "type": "string", "value": "enter-pollinations-ai"},
{"key": "$metadata.statusCode", "operation": "gte", "type": "number", "value": 400}
],
"calculations": [{"operator": "count"}],
"groupBys": [
{"type": "string", "value": "$metadata.statusCode"},
{"type": "string", "value": "$metadata.error"}
],
"limit": 50
}
}' | jq '.result.events.events[:20]'curl -s "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/observability/telemetry/query" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"timeframe": {
"from": '$(( $(date +%s) - 3600 ))'000,
"to": '$(date +%s)'000
},
"parameters": {
"datasets": ["workers"],
"filters": [
{"key": "$workers.scriptName", "operation": "eq", "type": "string", "value": "enter-pollinations-ai"},
{"key": "$metadata.statusCode", "operation": "gte", "type": "number", "value": 400}
],
"calculations": [{"operator": "count"}],
"groupBys": [
{"type": "string", "value": "model"},
{"type": "string", "value": "$metadata.statusCode"}
],
"limit": 100
}
}' | jq '.result.calculations[0].aggregates'curl -s "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/observability/telemetry/query" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"timeframe": {
"from": '$(( $(date +%s) - 900 ))'000,
"to": '$(date +%s)'000
},
"parameters": {
"datasets": ["workers"],
"filters": [
{"key": "$workers.scriptName", "operation": "eq", "type": "string", "value": "enter-pollinations-ai"},
{"key": "$metadata.statusCode", "operation": "gte", "type": "number", "value": 500}
],
"limit": 20
}
}' | jq '.result.events.events[] | {
timestamp: .timestamp,
statusCode: ."$metadata".statusCode,
error: ."$metadata".error,
message: ."$metadata".message,
requestId: ."$workers".requestId,
url: ."$metadata".url
}'curl -s "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/observability/telemetry/keys" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"timeframe": {
"from": '$(( $(date +%s) - 3600 ))'000,
"to": '$(date +%s)'000
},
"datasets": ["workers"],
"filters": [
{"key": "$workers.scriptName", "operation": "eq", "type": "string", "value": "enter-pollinations-ai"}
]
}' | jq '.result.keys'log.warn("Chat completions error {status}: {body}", {
status: response.status,
body: responseText,
});# Get model health stats (last 5 minutes)
curl "https://api.europe-west2.gcp.tinybird.co/v0/pipes/model_health.json?token=$TINYBIRD_TOKEN" | jq '.data'
# Get detailed error breakdown
curl "https://api.europe-west2.gcp.tinybird.co/v0/pipes/model_errors.json?token=$TINYBIRD_TOKEN" | jq '.data'apps/model-monitor/src/hooks/useModelMonitor.js# Filter by request ID
curl -s "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/observability/telemetry/query" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"timeframe": {"from": '$(( $(date +%s) - 86400 ))'000, "to": '$(date +%s)'000},
"parameters": {
"datasets": ["workers"],
"filters": [
{"key": "$workers.requestId", "operation": "eq", "type": "string", "value": "REQUEST_ID_HERE"}
],
"limit": 100
}
}' | jq '.result.events.events'# Image service
ssh enter-services "sudo journalctl -u image-pollinations.service --since '5 minutes ago'"
# Text service
ssh enter-services "sudo journalctl -u text-pollinations.service --since '5 minutes ago'"TOKEN=$(grep ENTER_API_TOKEN_REMOTE enter.pollinations.ai/.testingtokens | cut -d= -f2)
# Test text model
curl -s 'https://gen.pollinations.ai/v1/chat/completions' \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"model": "MODEL_NAME", "messages": [{"role": "user", "content": "Test"}]}' \
-w "\nHTTP: %{http_code}\n"
# Test image model
curl -s 'https://gen.pollinations.ai/image/test?model=MODEL_NAME&width=256&height=256' \
-H "Authorization: Bearer $TOKEN" \
-w "\nHTTP: %{http_code}\n" -o /dev/null/telemetry/keys/telemetry/valuesenter.pollinations.ai/secrets/env.json/telemetry/queryqueryIdwrangler tailapps/model-monitor/src/hooks/useModelMonitor.jsenter.pollinations.ai/observability/.tinybtoken# Public read-only token from apps/model-monitor
TINYBIRD_TOKEN="p.eyJ1IjogImFjYTYzZjc5LThjNTYtNDhlNC05NWJjLWEyYmFjMTY0NmJkMyIsICJpZCI6ICJmZTRjODM1Ni1iOTYwLTQ0ZTYtODE1Mi1kY2UwYjc0YzExNjQiLCAiaG9zdCI6ICJnY3AtZXVyb3BlLXdlc3QyIn0.Wc49vYoVYI_xd4JSsH_Fe8mJk7Oc9hx0IIldwc1a44g"
# Get model health (last 5 min)
curl -s "https://api.europe-west2.gcp.tinybird.co/v0/pipes/model_health.json?token=$TINYBIRD_TOKEN" | jq '.data'generation_event.tinyb# Get admin token from .tinyb file
TINYBIRD_ADMIN_TOKEN=$(jq -r '.token' enter.pollinations.ai/observability/.tinyb)
# Find users with frequent 403 errors (last 24 hours)
curl -s "https://api.europe-west2.gcp.tinybird.co/v0/sql?token=$TINYBIRD_ADMIN_TOKEN" \
--data-urlencode "q=SELECT user_id, user_github_username, user_tier, count() as error_403_count
FROM generation_event
WHERE response_status = 403
AND start_time > now() - interval 24 hour
AND user_id != ''
AND user_id != 'undefined'
GROUP BY user_id, user_github_username, user_tier
ORDER BY error_403_count DESC
LIMIT 20"
# Find users with 500 errors (actual backend issues)
curl -s "https://api.europe-west2.gcp.tinybird.co/v0/sql?token=$TINYBIRD_ADMIN_TOKEN" \
--data-urlencode "q=SELECT user_github_username, model_requested, error_message, count() as error_count
FROM generation_event
WHERE response_status >= 500
AND start_time > now() - interval 24 hour
GROUP BY user_github_username, model_requested, error_message
ORDER BY error_count DESC
LIMIT 20"
# Check specific user's recent errors
curl -s "https://api.europe-west2.gcp.tinybird.co/v0/sql?token=$TINYBIRD_ADMIN_TOKEN" \
--data-urlencode "q=SELECT start_time, response_status, model_requested, error_message
FROM generation_event
WHERE user_github_username = 'USERNAME_HERE'
AND start_time > now() - interval 24 hour
ORDER BY start_time DESC
LIMIT 50"generation_evententer.pollinations.ai/observability/datasources/generation_event.datasourceuser_iduser_github_usernameuser_tierresponse_statuserror_messageerror_response_codemodel_requestedmodel_usedtotal_pricetotal_coststart_timeend_timeresponse_time# Find users with >10 403 errors in last 24 hours
.claude/skills/model-debugging/scripts/find-403-users.sh 24 10
# Filter by tier (e.g., only spore users)
.claude/skills/model-debugging/scripts/find-403-users.sh 24 10 spore# Find 500+ errors grouped by user/model/message
.claude/skills/model-debugging/scripts/find-500-errors.sh 24# See a user's recent errors
.claude/skills/model-debugging/scripts/check-user-errors.sh superbrainai 24| Model | Type | Endpoint | Status |
|---|---|---|---|
| text | POST /v1/chat/completions | ✅ |
| text | POST /v1/chat/completions | ✅ |
| text | POST /v1/chat/completions | ✅ |
| text | GET /text/{prompt}?model=openai-audio&voice=alloy | ✅ (MP3) |
| text | POST /v1/chat/completions | ✅ |
| text | POST /v1/chat/completions | ✅ |
| image | GET /image/{prompt} | ✅ |
| image | GET /image/{prompt} | ✅ |
| image | GET /image/{prompt} | ✅ |
| video | GET /image/{prompt} | ✅ (MP4) |