Support

Troubleshooting

Common issues and their solutions when using CellCMS.

Database

Connection refused

Symptom: Error: connect ECONNREFUSED 127.0.0.1:5432

Causes:

  • PostgreSQL is not running. Start it or check Docker.
  • Wrong DATABASE_URL. Verify the host, port, username, and password.
  • Verify the DATABASE_URL host, port, username, and password are correct.

Migration errors

Symptom: Schema errors when starting or running pnpm migrate:up.

Fixes:

  • Ensure you're running PostgreSQL 15+
  • Check that the cellcms database exists:
    psql -U cellcms -l
    
  • Run the migration manually:
    psql $DATABASE_URL < migrations/001_initial-schema.sql
    

Duplicate key errors

Symptom: duplicate key value violates unique constraint

This can happen during re-imports. Use the ?replace=true query parameter:

curl -X POST "https://api.cellcms.com/api/v1/data/import/production?replace=true" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/x-ndjson" \
  --data-binary @data.ndjson

Authentication

JWT expired

Symptom: 401 Unauthorized responses after a period of inactivity.

Access tokens expire after 15 minutes by default. Use the refresh token to get a new pair:

curl -X POST https://api.cellcms.com/api/v1/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{"refreshToken": "your-refresh-token"}'

If the refresh token has also expired (7 days), you need to log in again.

Adjusting lifetimes:

JWT_ACCESS_EXPIRES_IN=1h
JWT_REFRESH_EXPIRES_IN=30d

Token refresh failing

Symptom: Refresh endpoint returns 401.

  • Refresh tokens are single-use — they're rotated on each refresh. If you used the same token twice, it's invalid.
  • Refresh tokens expire after 7 days by default.
  • After logout, the refresh token is revoked.

API token not working

Symptom: 401 when using a cell_... token.

  • Verify the token is correct — it's only shown once at creation
  • Check that the token hasn't been revoked
  • Check permissions — a read-only token can't create documents
  • Check dataset scope — if the token is scoped to a dataset, it can't access other datasets

Default password doesn't work

The default admin credentials are:

  • Email: admin@cellcms.com
  • Password: admin

If these don't work:

  • The password may have been changed
  • The migration may not have run (the admin user is created by the initial migration)
  • Check docker logs cellcms-postgres for migration errors

GROQ Queries

Parse errors

Symptom: 400 Bad Request with a parse error message.

Common mistakes:

  • Missing quotes: String values in filters need quotes: *[_type == "post"] (not *[_type == post])
  • Wrong brackets: Filters use [], projections use {}: *[_type == "post"]{title, slug}
  • Invalid parameter: Parameters use $: *[_type == $type] with params: { type: "post" }

Missing parameters

Symptom: 400 Bad Request — "Missing parameter: $slug"

Ensure all $param references in the query have corresponding values in the params object:

{
  "query": "*[_type == \"post\" && slug.current == $slug][0]",
  "params": { "slug": "hello-world" }
}

Unsupported features

If a GROQ feature isn't supported, you'll get a parse or execution error. See the GROQ Reference for the full list of supported features.

Workarounds:

  • Use multiple simpler queries instead of complex ones
  • Compute derived values in your application code
  • For unsupported functions, fetch the raw data and process client-side

Assets

Upload failures

Symptom: 400 Bad Request on asset upload.

  • No file field: Ensure the multipart form field is named file
  • File too large: Max upload size is 50 MB
  • Project not found: The dataset must belong to an existing project

Transform errors

Symptom: Image transform returns an error or unexpected result.

  • Only image files can be transformed (JPEG, PNG, WebP, AVIF, GIF, TIFF)
  • Check that transform parameters are valid numbers
  • Quality must be 1–100
  • Very large images may cause memory issues — consider uploading pre-optimized versions

Missing assets after import

If you imported documents from Sanity but images aren't showing, you need to upload the assets separately. See Migrating from Sanity.


WebSocket

Connection drops

Symptom: WebSocket disconnects frequently.

  • Reverse proxy timeout: Nginx closes idle connections after 60 seconds by default. Set a longer timeout:
    location /ws {
        proxy_read_timeout 86400;
        proxy_send_timeout 86400;
    }
    
  • Token expiry: WebSocket connections authenticate on connect. If the JWT expires, the connection may be closed.

Presence not updating

Symptom: Other users' avatars don't appear in the editor.

  • Verify WebSocket is connected (green dot in Studio top bar)
  • Check that both users are on the same dataset
  • Check browser console for WebSocket errors

Studio

Blank screen

Symptom: Studio loads but shows a white screen.

  • Open browser DevTools → Console for JavaScript errors
  • Check that the API is reachable from the browser
  • Verify CORS: the CORS_ORIGIN must match the Studio URL exactly
  • Hard refresh: Ctrl+Shift+R / Cmd+Shift+R

Schema not loading

Symptom: No document types appear in the sidebar.

  • Check that schema types are registered with the server
  • Verify the project exists: GET /api/v1/projects
  • Check API logs for schema compilation errors

CORS errors

Symptom: Browser console shows Access-Control-Allow-Origin errors.

Set CORS_ORIGIN to your exact Studio URL:

# Correct
CORS_ORIGIN=https://studio.cellcms.com

# Wrong (trailing slash)
CORS_ORIGIN=https://studio.cellcms.com/

# Wrong (different URL)
CORS_ORIGIN=https://cellcms.com

Restart the API after changing CORS settings.

Auto-save not working

Symptom: Changes aren't automatically saved.

Auto-save triggers 2 seconds after the last edit. If it's not working:

  • Check for validation errors (required fields, etc.)
  • Verify you have editor permissions
  • Check browser console for API errors

Performance

Slow queries

  • Add projections to limit returned fields: *[_type == "post"]{title, slug} instead of *[_type == "post"]
  • Use slicing to paginate: *[_type == "post"] | order(_createdAt desc)[0...20]
  • Check PostgreSQL indexes with EXPLAIN ANALYZE

High memory usage

  • Large file uploads can consume memory during processing
  • Image transforms are memory-intensive — Sharp processes images in memory
  • Consider increasing container memory limits for high-traffic deployments

Getting Help