Skip to content

Troubleshooting

This page covers the most common issues you may encounter when running club and how to resolve them.

Server Won’t Start

”SERVER_URL is required”

The server requires a public URL to construct archive download URLs and upload redirects.

Fix: Set the SERVER_URL environment variable to the exact URL clients use — scheme and host must match:

Terminal window
SERVER_URL=https://packages.example.com

”JWT_SECRET is required” or “must be at least 32 characters”

The session secret is used to sign tokens and must be at least 32 characters long.

Fix: Generate a secure secret:

Terminal window
JWT_SECRET=$(openssl rand -hex 32)

“POSTGRES_URL must be set when DB_BACKEND=postgres”

You set the database backend to PostgreSQL but did not provide a connection URL.

Fix: Either set the PostgreSQL URL or switch back to SQLite:

Terminal window
# Option A: Provide the PostgreSQL URL
POSTGRES_URL=postgres://club:secret@db.example.com:5432/club
# Option B: Use SQLite (default)
DB_BACKEND=sqlite

“Setup code lost” / cannot complete first-run setup

The first-run setup flow prints a random setup code to the server logs and is IP-pinned after the verify step — only the IP that verified the code can complete setup.

Fix:

  1. Scan the server logs for the setup code (look for a line printed during startup or the first visit to the setup page).
  2. Make sure you are browsing from the same IP that was pinned during verify. If your IP changed (VPN, new network), restart the server to issue a fresh code.
  3. If the log is gone and setup is not yet complete, restart the container — a new code is printed on boot until setup finishes.

Port already in use

Another process is using port 8080 (or your configured port).

Fix:

Terminal window
# Find what's using the port
lsof -i :8080
# Either stop that process or change the club listen port
LISTEN_PORT=3000

Permission denied on database or data directory

The club process does not have write access to the data directory.

Fix:

Terminal window
# Check and fix permissions
ls -la /data/
chown -R 1000:1000 /data/
# In Docker, ensure the volume is writable
docker run --rm -v club-data:/data alpine ls -la /data/

dart pub publish Fails

”Authentication required”

The dart pub client does not have a valid token for your club server.

Fix: Add a token for your server:

Terminal window
dart pub token add https://packages.example.com
# When prompted, enter your club API token (club_pat_...)

Or set it via environment variable in CI:

Terminal window
export PUB_TOKEN=club_pat_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4
dart pub token add https://packages.example.com --env-var PUB_TOKEN

“403 Forbidden” / “Not authorized to publish this package”

Your token does not have write scope, or you are not an authorized uploader for the package.

Fix:

  1. Verify your token has write scope (web UI → Settings → API Keys).
  2. Ask an existing uploader or admin to add you as an uploader on the package.
  3. If the package is publisher-owned, ensure you are a member of the publisher.

publish_to pointing at the wrong server

If pubspec.yaml lacks publish_to: or points at https://pub.dev, dart pub publish will try to push to pub.dev instead of your club instance.

Fix: Set publish_to in your package’s pubspec.yaml:

publish_to: https://packages.example.com

“Version 2.1.0 already exists”

You are trying to publish a version that already exists with different content.

Fix: Bump the version number in your pubspec.yaml and publish again. Published versions are immutable.

”Invalid package name”

Package names must be lowercase alphanumeric with underscores, 1-64 characters.

Fix: Rename your package in pubspec.yaml to follow the naming rules:

name: my_package # Valid
# name: My-Package # Invalid (uppercase, hyphens)
# name: my.package # Invalid (dots)

Publish uses the wrong pubspec (e.g. example/pubspec.yaml)

club rejects archives that contain a pubspec.yaml anywhere other than the root. If your archive build is pulling in a nested pubspec.yaml (for example from example/), the publish is rejected.

Fix:

  • Ensure only the root pubspec.yaml is included in the archive.
  • Add nested paths to .pubignore if they should not be packaged.

”413: Payload too large”

The tarball exceeds the maximum upload size.

Fix: Either reduce the package size (remove unnecessary files using .pubignore) or increase the server limit:

Terminal window
MAX_UPLOAD_BYTES=524288000 # 500 MB

Upload hangs or times out

Upload sessions expire after 10 minutes. Large packages on slow connections may not finish in time.

Fix:

  1. Check your network connection speed
  2. Reduce package size using .pubignore
  3. Increase upload size limit if needed
  4. Check if a reverse proxy has a smaller timeout or body size limit than club

dart pub get Fails

”Could not resolve package”

The package does not exist on your club server, or the version constraint cannot be satisfied.

Fix:

  1. Verify the package exists: curl -H "Authorization: Bearer $TOKEN" https://packages.example.com/api/packages/<package>
  2. Check available versions match your constraint
  3. Ensure your pubspec.yaml points to the correct hosted URL:
dependencies:
my_package:
hosted: https://packages.example.com
version: ^1.0.0

“Connection refused” or “Could not connect”

The club server is not reachable from the client.

Fix:

  1. Verify the server is running: curl https://packages.example.com/api/v1/health
  2. Check DNS resolution: nslookup packages.example.com
  3. Check firewall rules (port 443 for HTTPS, port 8080 for direct access)
  4. If behind a VPN, ensure the VPN is connected

TLS / SSL certificate errors

HandshakeException: Handshake error in client

Fix:

  1. Ensure your TLS certificate is valid and not expired.
  2. For self-signed certificates, configure Dart to trust them:
    Terminal window
    export DART_VM_OPTIONS="--root-certs-file=/path/to/ca-cert.pem"
  3. Check that the certificate covers the hostname you are using.
  4. If you set TRUST_PROXY=true but the proxy is not actually terminating TLS, Secure cookies and redirect URLs can end up mismatched. Either terminate TLS at the proxy or disable TRUST_PROXY.

Authentication Issues

401: “Authentication required”

Possible causes:

  • Token is missing from the request
  • Token has been revoked
  • Token has expired

Diagnosis:

Terminal window
# Check if your token works
curl -H "Authorization: Bearer $TOKEN" \
https://club.example.com/api/auth/keys

If this returns 401, the token is invalid. Create a new one.

403: “Insufficient permissions”

Possible causes:

  • Token has read scope but the operation requires write or admin
  • User is not an uploader of the target package
  • User is not a member of the publisher
  • Scopes were clamped to a lower user role at creation time

Diagnosis:

Terminal window
# List your API keys to check scopes
curl -H "Authorization: Bearer $TOKEN" \
https://club.example.com/api/auth/keys

Check the scopes field in the response. Create a new token with the required scopes if needed.

Signed-in web UI loses the session on every refresh

Possible causes:

  • Your deployment is serving CLUB over plain HTTP — secure cookies will refuse to stick.
  • The browser is blocking third-party cookies (relevant only if CLUB is embedded in an iframe on another domain).
  • A reverse proxy in front of CLUB is stripping cookies or the X-Forwarded-Proto header without TRUST_PROXY=true set.

Fix: Run CLUB behind HTTPS (see TLS setup). If you’re behind a reverse proxy, set TRUST_PROXY=true.


Database Issues

”database is locked” (SQLite)

SQLite allows only one writer at a time. Under heavy concurrent write load, you may see lock contention.

Possible causes:

  • Multiple processes accessing the same database file
  • Heavy concurrent publish operations
  • Backup process holding a lock

Fix:

  1. Ensure only one club process accesses the database file.
  2. The default busy_timeout is 5 seconds — most locks resolve within this time.
  3. If the problem persists under normal load, consider running with a larger disk or splitting workloads.

PostgreSQL connection refused

Fix:

  1. Verify PostgreSQL is running: pg_isready -h db.example.com
  2. Check the connection URL format: postgres://user:pass@host:5432/dbname
  3. Verify network connectivity between club and PostgreSQL
  4. Check PostgreSQL’s pg_hba.conf allows connections from the club host

Out of Disk Space

Symptoms

  • Health check reports blob_store error
  • Publish operations fail with internal errors
  • SQLite reports “database or disk is full”

Fix

  1. Check disk usage:

    Terminal window
    df -h /data
    du -sh /data/*
  2. Free space immediately:

    Terminal window
    # Clean up temp files
    rm -rf /tmp/club-uploads/*
    # Remove SQLite WAL file (checkpoints first)
    sqlite3 /data/club.db "PRAGMA wal_checkpoint(TRUNCATE);"
  3. Long-term solutions:

    • Increase disk size
    • Switch to S3 or GCS for blob storage
    • Delete old/unused package versions via the admin API
    • Set up disk usage monitoring and alerts (see Monitoring)

Search Not Returning Results

The search index is updated during publish finalization. If a package was published but does not appear in search:

  1. Wait a moment. The index update happens synchronously during publish, but if you are using Meilisearch, there may be a brief indexing delay.

  2. Check if the package is unlisted:

    Terminal window
    curl -H "Authorization: Bearer $TOKEN" \
    https://packages.example.com/api/packages/<package>/options

    If isUnlisted is true, the package will not appear in search results.

  3. Rebuild the search index by restarting the server.

Search returns no results at all

  1. Verify the search backend is healthy:

    Terminal window
    curl https://packages.example.com/api/v1/health

    Check the search_index component in the response.

  2. If using Meilisearch, verify the Meilisearch instance is running:

    Terminal window
    curl http://meilisearch:7700/health
  3. If using SQLite FTS5, the index may be corrupted. Rebuild it:

    Terminal window
    sqlite3 /data/club.db "INSERT INTO package_fts(package_fts) VALUES ('rebuild');"

Getting Help

If you cannot resolve an issue:

  1. Check the server logs for detailed error messages:

    Terminal window
    docker compose logs club --tail 200
  2. Set log level to debug for more detail:

    Terminal window
    LOG_LEVEL=debug
  3. Verify your configuration is valid by checking startup logs for validation errors.

  4. Open an issue on the GitHub repository with:

    • club version (from the health endpoint)
    • Storage backend configuration (SQLite/PostgreSQL, filesystem/S3/GCS)
    • Relevant log output (redact any secrets)
    • Steps to reproduce the issue