Skip to content

Troubleshooting

This guide helps you diagnose and fix common issues in FraiseQL applications.

If FraiseQL isn’t working, start with fraiseql doctor — it runs checks covering schema, config, database, secrets, and feature coherence in a single command:

Terminal window
fraiseql doctor

If doctor passes but the issue persists, continue with the manual checklist below. To update the schema without restarting, use kill -USR1 or the admin reload endpoint.

  1. Check the FraiseQL version

    Terminal window
    fraiseql --version
  2. Validate schema JSON structure

    Terminal window
    fraiseql validate schema.json

    fraiseql validate checks the compiled schema JSON for valid type references and circular dependencies. It does not connect to the database. To check your database connection, run:

    Terminal window
    psql $DATABASE_URL -c "SELECT 1"
  3. Compile schema with verbose output

    Terminal window
    fraiseql compile --verbose

Error example:

Error: Syntax error in schema
→ Line 5: expected ':'

Cause: Python syntax error in schema file.

Fix: Check the indicated line for syntax issues:

# Wrong
@fraiseql.type
class User
id: str
# Correct
@fraiseql.type
class User:
id: str

Error example:

Error: Type 'Author' not found
-> Referenced in Post.author but not defined

Cause: Referenced type doesn’t exist in schema.

Fix: Define the missing type or fix the reference:

@fraiseql.type
class User: # Define the type
id: str
name: str
@fraiseql.type
class Post:
author: User # Now valid

Error example:

Error: Circular reference detected
-> User -> Post -> User

Cause: Types reference each other in a cycle.

Fix: Use forward references with strings:

@fraiseql.type
class User:
id: str
posts: list['Post'] # Forward reference
@fraiseql.type
class Post:
author: 'User' # Forward reference
# Error: Missing colon after class definition
@fraiseql.type
class User # ← SyntaxError: expected ':'
id: str
# Fix:
@fraiseql.type
class User:
id: str
# Error: Wrong annotation syntax
@fraiseql.type
class Post:
tags: List[str] # ← NameError: 'List' not defined
# Fix: use built-in list
@fraiseql.type
class Post:
tags: list[str]

Error:

Error: Failed to connect to database
-> Connection refused (os error 111)

Causes:

  1. PostgreSQL not running
  2. Wrong connection URL
  3. Network/firewall issues
  1. Check the server is running:

    Terminal window
    pg_isready -h localhost -p 5432
  2. Test the connection string directly:

    Terminal window
    psql $DATABASE_URL -c "SELECT 1"
  3. Verify the URL format:

    postgresql://user:pass@host:port/dbname

Error example:

Error: Migration failed
-> relation "tb_user" already exists

Cause: Table already exists from previous migration.

  1. Check migration status to understand what has already been applied:

    Terminal window
    fraiseql migrate status
  2. If the state is inconsistent, reset the migration history (CAUTION: drops data in development only):

    Terminal window
    fraiseql migrate reset
  3. Alternatively, remove the conflicting object manually:

    Terminal window
    psql $DATABASE_URL -c "DROP TABLE IF EXISTS tb_user CASCADE"

Error example:

Error: column "pk_user" does not exist
-> Referenced in view tv_user definition

Cause: View references non-existent column.

Fix: Check table schema matches view definition:

-- Check table columns
\d tb_user
-- Ensure column exists
ALTER TABLE tb_user ADD COLUMN pk_user BIGINT;

Error:

Error: function fn_create_user(unknown, unknown) does not exist

Cause: Function signature mismatch.

  1. Inspect the existing function signatures:

    \df fn_create_user
  2. Recreate the function with explicit parameter types:

    CREATE OR REPLACE FUNCTION fn_create_user(
    user_email TEXT,
    user_name TEXT
    ) RETURNS mutation_response AS $$
    ...

Error:

{
"errors": [{
"message": "Cannot query field 'username' on type 'User'. Did you mean 'name'?"
}]
}

Cause: Querying non-existent field.

Fix: Use correct field name from schema:

# Wrong
query { users { username } }
# Correct
query { users { name } }

Error:

{
"errors": [{
"message": "Variable '$id' expected type 'ID!' but got 'String'"
}]
}

Cause: Variable type doesn’t match schema.

Fix: Use correct type in query:

# Check schema for expected type
query GetUser($id: ID!) { # ID!, not String!
user(id: $id) { name }
}

Error:

{
"errors": [{
"message": "Cannot return null for non-nullable field User.email"
}]
}

Cause: Database returned NULL for required field.

Fix: Either fix data or update schema:

# Option 1: Make field nullable
email: str | None
# Option 2: Ensure data is never null
INSERT INTO tb_user (email, ...) VALUES ('required@email.com', ...)

Error:

{
"errors": [{
"message": "Email and name are required"
}]
}

Cause: SQL function validation rejected input.

Fix: Provide required fields:

mutation {
createUser(
email: "user@example.com", # Required
name: "User Name" # Required
) { id }
}

Error:

{
"errors": [{
"message": "User with email user@example.com already exists"
}]
}

Cause: Duplicate value for unique column.

Fix: Use unique value or update existing:

# Check if exists first, then update or create
mutation {
updateUser(id: "existing-id", name: "New Name") { id }
}

Error:

{
"errors": [{
"message": "Author not found"
}]
}

Cause: Referenced entity doesn’t exist.

Fix: Ensure parent entity exists:

# First create user
mutation { createUser(email: "...", name: "...") { id } }
# Then create post with valid author
mutation { createPost(authorId: "valid-user-id", ...) { id } }

Error:

{
"errors": [{
"message": "Too many variables: request contains more than 1000 variables"
}]
}

Cause: The variables JSON object has more than 1,000 top-level keys. This is a hard security limit that cannot be configured.

Fix: Restructure your query to pass bulk data as arrays instead of individual variables:

# Instead of $id1, $id2, ... $id1500
query GetUsers($ids: [ID!]!) {
users(ids: $ids) { id name }
}

Symptom: Queries take > 100ms.

Diagnosis:

-- Enable query logging
ALTER SYSTEM SET log_min_duration_statement = 100;
SELECT pg_reload_conf();
-- Check slow query log
tail -f /var/log/postgresql/postgresql.log

Common fixes:

  1. Add indexes:
CREATE INDEX idx_tv_user_email ON tv_user ((data->>'email'));
  1. Use pagination:
query { users(limit: 20, offset: 0) { id } }
  1. Simplify query:
# Instead of deeply nested
query { posts { author { posts { author { ... } } } } }
# Use separate queries
query { posts { authorId } }
query { users(ids: [...]) { name } }

Error example:

Error: pool exhausted
-> no connections available after 30s

Cause: Too many concurrent queries.

Fix:

# Increase pool size
[database]
pool_max = 100 # Increase from default
# Or use external pooling
# PgBouncer, pgpool-II

Symptom: OOM errors, high memory usage.

Diagnosis:

Terminal window
# Check process memory
ps aux | grep fraiseql
# Check PostgreSQL memory
SELECT pg_size_pretty(pg_database_size('mydb'));

Fixes:

  1. Limit query complexity:
[validation]
max_query_depth = 5
max_query_complexity = 500
  1. Add pagination limits:
[query_defaults]
limit = true # ensure limit argument is enabled so clients can paginate

Error:

{
"errors": [{
"message": "Invalid token"
}]
}

Causes:

  1. Token expired
  2. Wrong signing secret
  3. Malformed token

Fix:

Terminal window
# Debug token
echo $TOKEN | cut -d. -f2 | base64 -d | jq
# Check expiry
# "exp": 1704067200 (Unix timestamp)
# Verify secret matches
# Server uses JWT_SECRET env var

Error:

{
"errors": [{
"message": "Unauthorized"
}]
}

Cause: Missing or invalid Authorization header.

Fix:

Terminal window
curl -X POST http://localhost:8080/graphql \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"query": "{ users { id } }"}'

Error:

{
"errors": [{
"message": "Forbidden: missing scope read:User.email"
}]
}

Cause: Token lacks required scope.

Fix: Request token with required scopes:

# Include scope in token
jwt.encode({
"sub": user_id,
"scope": "read:User.email read:User.name"
}, secret)

Set the RUST_LOG environment variable before starting FraiseQL:

Terminal window
RUST_LOG=debug fraiseql run

Always check for partial errors:

{
"data": {
"users": [...]
},
"errors": [
{"message": "Some field failed"}
]
}
-- Log all queries (development only)
ALTER SYSTEM SET log_statement = 'all';
SELECT pg_reload_conf();
Terminal window
# Test endpoint
curl -v http://localhost:8080/graphql
# Check headers
curl -I http://localhost:8080/health
# Test with specific request
curl -X POST http://localhost:8080/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ __schema { types { name } } }"}'

If you can’t resolve an issue:

  1. Search existing issues: https://github.com/fraiseql/fraiseql/issues
  2. Check documentation: https://fraiseql.dev/docs
  3. Open an issue with:
    • FraiseQL version (fraiseql --version)
    • Error message (full stack trace)
    • Minimal reproduction
    • Expected vs actual behavior

Performance Optimization

Optimize slow queries and reduce database load in production. Performance Guide

Testing Guide

Write tests to prevent regressions and catch issues early. Testing Guide

Deployment Guide

Production configuration and deployment best practices. Deployment Guide

GitHub Issues

Search for known bugs and report new ones with your diagnostics bundle. Open an Issue

Discord Community

Get real-time help from maintainers and community members. Join Discord

Common Issues

Comprehensive reference organised by error type and symptom. Common Issues

REST endpoint returns 404

  • Verify the rest_path annotation is present on the query or mutation
  • Check the URL path — default is /rest/v1/; configurable via [rest] path
  • Confirm [rest] is enabled in schema TOML (v2.1+)

REST response has unexpected format

  • FraiseQL’s REST transport returns JSON in a {"data": ..., "meta": ..., "links": ...} envelope. If you expected unwrapped JSON arrays, update your client to read from the data field.
  • If you’re getting {"data": {"posts": [...]}} (nested data), you may be hitting the GraphQL endpoint (/graphql) instead of the REST endpoint (/rest/v1/...).

REST auth failing but GraphQL auth works

  • REST uses the same auth mechanism as GraphQL. Check that the Authorization: Bearer <token> or X-API-Key: <key> header is present.
  • Verify require_auth in [rest] is set to true when you expect auth enforcement.

gRPC connection refused or receives an HTTP/1.1 response

  • gRPC requires HTTP/2. Verify your load balancer or reverse proxy supports HTTP/2 pass-through.
  • Confirm the binary was built with the grpc-transport Cargo feature enabled.
  • On nginx: use listen 443 ssl http2; and grpc_pass directive instead of proxy_pass.

gRPC method not found

  • gRPC endpoints are auto-generated for all operations when [grpc] is enabled. Verify the operation name matches what gRPC clients expect — use grpcurl -plaintext localhost:50052 list to inspect available services.

See Observability for Prometheus metrics, OpenTelemetry tracing, and structured logging — these are the primary tools for diagnosing production issues.