Skip to main content

Overview

Platzi Viewer relies entirely on Google Drive API to stream course content. This page covers common Drive API errors and how to resolve them.

Authentication Errors

Error Message:
503 Drive service not available. Check service_account.json, 
GOOGLE_SERVICE_ACCOUNT_FILE or GOOGLE_SERVICE_ACCOUNT_JSON.
Check health endpoint:
curl http://localhost:8080/api/health
Response:
{
  "drive": {
    "available": false,
    "error": "Service account file not found. ..."
  }
}
Causes and solutions:1. Service account file not found:The server searches for service_account.json in:
  • Path specified in GOOGLE_SERVICE_ACCOUNT_FILE environment variable
  • Executable directory (for .exe builds)
  • Current working directory
  • Script directory
Solution:
# Option 1: Place file in the same directory as server.py
cp /path/to/service_account.json .

# Option 2: Set environment variable
# Windows
$env:GOOGLE_SERVICE_ACCOUNT_FILE="C:\path\to\service_account.json"
python server.py

# Linux/Mac
GOOGLE_SERVICE_ACCOUNT_FILE=/path/to/service_account.json python server.py
2. Invalid JSON format:Error detail: "GOOGLE_SERVICE_ACCOUNT_JSON is not valid JSON"Verify the JSON file is valid:
# Check JSON syntax
python -m json.tool service_account.json
Common issues:
  • Missing commas
  • Trailing commas (not allowed in JSON)
  • Unescaped quotes in strings
  • UTF-8 BOM (byte order mark) at start of file
3. Wrong credentials format:The file must be a service account key, not an OAuth client ID.Correct format:
{
  "type": "service_account",
  "project_id": "your-project-id",
  "private_key_id": "...",
  "private_key": "-----BEGIN PRIVATE KEY-----\n...",
  "client_email": "[email protected]",
  "client_id": "...",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs"
}
How to get the correct file:
  1. Go to Google Cloud Console
  2. Select your project
  3. Navigate to IAM & Admin > Service Accounts
  4. Click on your service account
  5. Go to Keys tab
  6. Click Add Key > Create new key
  7. Select JSON format
  8. Download and save as service_account.json
For Docker or environments where file mounting is difficult:Pass the entire JSON as an environment variable:
# Export as a single line (no newlines except in private_key)
export GOOGLE_SERVICE_ACCOUNT_JSON='{"type":"service_account",...}'
python server.py
In docker-compose.yml:
services:
  platzi-viewer:
    environment:
      - GOOGLE_SERVICE_ACCOUNT_JSON=${GOOGLE_SERVICE_ACCOUNT_JSON}
In .env file (for docker-compose):
GOOGLE_SERVICE_ACCOUNT_JSON={"type":"service_account",...}
Escape newlines in private_key as \n. Do not use actual line breaks.
Symptom: Health check passes but no courses load, or 403 errors when accessing files.Cause: The service account doesn’t have access to the shared Drive folder.Solution:
  1. Find your service account email:
  2. Share the Drive folder:
    • Open Google Drive in browser
    • Navigate to the root course folder (ID: 17kPqqPSheDtQ5S1HM6Qvvh2qJ7O3YADm in README example)
    • Right-click > Share
    • Add the service account email
    • Set permission to Viewer or Reader
    • Click Send (even though it’s not a real user)
  3. Verify access:
    python rebuild_cache_drive.py
    
    The script should start scanning folders without errors.
Error Message:
[WARN] Could not pre-refresh Drive credentials: ...
Usually harmless - the server will refresh tokens automatically on first API call.If persistent:
  • Check your system clock is correct (token validation is time-sensitive)
  • Verify internet connectivity
  • Regenerate service account key if very old (keys don’t expire, but can be revoked)

API Rate Limiting and Quotas

Symptom: Errors during cache rebuild or file streaming after heavy usage.Google Drive API quotas for service accounts:
  • Queries per minute: 12,000
  • Queries per day: 1,000,000,000 (effectively unlimited)
Check your quota usage:
  1. Go to Google Cloud Console
  2. Navigate to APIs & Services > Dashboard
  3. Click on Google Drive API
  4. View Quotas tab
If you hit limits during rebuild_cache_drive.py:
  • The script includes automatic throttling (~100 calls/second max)
  • Built-in retry logic with exponential backoff
  • Progress is saved in drive_scan_progress.json so you can resume
To resume interrupted scan:
python rebuild_cache_drive.py
# Automatically continues from last checkpoint
If regularly hitting limits:
  • Reduce scan frequency
  • Cache the results (don’t rebuild unnecessarily)
  • Request quota increase in Google Cloud Console
Symptom: Videos buffer excessively or fail to load during peak usage.Monitoring: Check server logs for:
[WARN] Drive API Error listing files (try 1/5): 429 Too Many Requests
Built-in mitigations:
  • Server uses retry logic with exponential backoff (up to 5 retries)
  • Shared authentication session to reduce token refresh overhead
  • HTTP Range Request support minimizes data transfer
Manual solutions:
  1. Reduce concurrent streams:
    • Don’t open multiple videos simultaneously
    • Close unused browser tabs
  2. Increase request quota (if you own the project):
  3. Use caching proxy (advanced):
    • Set up nginx or similar to cache Drive responses
    • Reduces duplicate requests for same content

File Access Errors

Error Message:
400 Invalid file ID
Causes:
  1. Local file reference (migration issue):
    400 Local file refs are disabled in Drive mode. 
    Rebuild cache with rebuild_cache_drive.py
    
    Solution: The cache was built with an older version that used local files. Rebuild:
    python rebuild_cache_drive.py
    
  2. Malformed Drive ID:
    • Valid Drive IDs match regex: [A-Za-z0-9_-]{10,}
    • Must be at least 10 characters
    • Only alphanumeric, underscore, and hyphen allowed
  3. Cache corruption:
    • Delete courses_cache.json and rebuild
Self-check endpoint:
http://localhost:8080/api/self-check-drive
Response:
{
  "ok": false,
  "message": "drive_only_issues_found",
  "totalRefs": 50000,
  "validDriveRefs": 49950,
  "localRefs": 50,
  "invalidRefs": 0,
  "issues": [
    {
      "location": "cat[0].route[1].course[0].mod[0].class[2].files.video",
      "value": "local:videos/intro.mp4",
      "reason": "local_ref_detected"
    }
  ]
}
Symptom: A course or class shows in the UI but videos/files won’t load.Possible causes:
  1. File was deleted from Drive:
    • Verify the file exists in Google Drive web interface
    • If deleted, rebuild cache to remove stale references
  2. File is in trash:
    • Files in Drive trash are excluded from listings
    • Restore the file or permanently delete and rebuild cache
  3. Cache is outdated:
    • Drive folder structure changed since last cache build
    • Rebuild:
      python rebuild_cache_drive.py
      
  4. Service account lost access:
    • Folder sharing was revoked
    • Re-share the folder with the service account email
Symptom: Files download instead of playing/displaying inline.Cause: Google Drive sometimes returns application/octet-stream for known file types.Automatic fallback: The server includes MIME type detection by file extension as a fallback.File type mappings in server.py:
extensions_map = {
    ".js": "application/javascript; charset=utf-8",
    ".mp4": "video/mp4",
    ".vtt": "text/vtt",
    ".html": "text/html; charset=utf-8",
    ".pdf": "application/pdf",
    # etc.
}
If a file type is not recognized:
  • It will be served as application/octet-stream
  • Browser will likely download it instead of displaying it
  • This is expected behavior for unknown types

Drive API Internal Errors

Error in server logs:
[WARN] Drive API Error listing files (try 1/5): 500 Internal Server Error
Cause: Google Drive API occasionally returns transient 500 errors.Automatic mitigation:
  • drive_service.py includes retry logic with exponential backoff
  • Up to 5 retries with delays: 2s, 4s, 8s, 16s, 32s
  • Most 500 errors resolve on retry
If errors persist:
Log patterns:
[ERROR] Drive streaming error for {fileId}: Connection reset by peer
OSError: [Errno 32] Broken pipe
Common causes:
  1. Client closed connection:
    • User navigated away from page
    • Browser stopped the request
    • Not an error - the server detects this and doesn’t log it
  2. Network interruption:
    • WiFi disconnection
    • VPN timeout
    • ISP issues
    • Solution: Retry the request or check network stability
  3. Drive API timeout:
    • Very large files or slow Drive response
    • Server timeout is 60s for streaming (timeout=(5, 60))
    • If file is extremely large, consider downloading separately
Not harmful if intermittent. Only concerning if happening frequently.
Error Message:
ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED]
Causes:
  1. System clock is wrong:
    • Certificate validation is time-sensitive
    • Sync your system clock
  2. Outdated CA certificates:
    pip install --upgrade certifi
    
  3. Corporate proxy or firewall:
    • Proxy is intercepting HTTPS traffic
    • May need to install corporate CA certificate
    • Or configure proxy settings:
      export HTTPS_PROXY=http://proxy.example.com:8080
      export HTTP_PROXY=http://proxy.example.com:8080
      python server.py
      
Never disable certificate verification in production. This makes you vulnerable to man-in-the-middle attacks.

Progress and Metadata Endpoints

Error Response:
{"error": "payload_too_large"}
Default limit: 2MB (MAX_PROGRESS_BYTES=2097152)Solution:
# Increase to 4MB
MAX_PROGRESS_BYTES=4194304 python server.py
Why so large?
  • Progress is stored as a JSON object with keys for every class
  • Large course catalogs can result in >1MB of progress data
  • The limit prevents abuse/DoS attacks
Request:
GET http://localhost:8080/api/cache-meta
Response with null mtime:
{
  "cache_file_path": "/path/to/courses_cache.json",
  "source": "viewer",
  "mtime": null,
  "mtimeEpoch": null,
  "stats": {...}
}
Cause: Cache file exists but couldn’t be stat’d (permissions or file system issue).Check:
ls -l courses_cache.json
# Ensure file exists and is readable

Debugging Tools

Health Endpoint

curl http://localhost:8080/api/health | python -m json.tool
Check:
  • drive.available - Should be true
  • drive.error - Should be null
  • ffmpeg.available - Needed for compatibility mode

Self-Check Endpoint

curl http://localhost:8080/api/self-check-drive | python -m json.tool
Validates:
  • All file references in cache are valid Drive IDs
  • No local file references (local:...)
  • No malformed IDs
Response interpretation:
  • ok: true - All references are valid
  • ok: false - Issues found, check issues array
  • Rebuild cache if localRefs > 0 or invalidRefs > 0

Manual Drive API Test

Test authentication:
from drive_service import drive_service

# This will print the source of credentials
print(f"Loaded from: {drive_service.service_account_source}")

# Test a simple API call
meta = drive_service.get_file_metadata("17kPqqPSheDtQ5S1HM6Qvvh2qJ7O3YADm")
print(f"Root folder name: {meta['name']}")
If this works but the server doesn’t:
  • Environment variable differences between shells
  • Python virtual environment not activated
  • Different working directories

Docker-Specific Issues

Problem: Works locally but not in Docker.Checklist:
  1. Volume mount is correct:
    volumes:
      - ./secrets/service_account.json:/app/service_account.json:ro
    
  2. Environment variable points to mounted path:
    environment:
      - GOOGLE_SERVICE_ACCOUNT_FILE=/app/service_account.json
    
    Not ./service_account.json or relative paths.
  3. File exists in container:
    docker exec -it <container> ls -l /app/service_account.json
    
  4. Permissions are readable:
    docker exec -it <container> cat /app/service_account.json
    
    Should print JSON content (or at least not “Permission denied”).

See Also

Build docs developers (and LLMs) love