chenhg5/cc-connect · error

create crypto helper: %w

Error message

create crypto helper: %w

What it means

This error wraps any failure from cryptohelper.NewCryptoHelper() when the Matrix platform adapter initializes its end-to-end-encryption helper. NewCryptoHelper builds a SQL crypto store (pickle key, device identity, olm/megolm machinery) at dbPath; if constructing the store, parsing the user ID, or opening the database fails, the error is wrapped with context and returned to initCrypto, aborting E2EE setup.

Source

Thrown at platform/matrix/e2ee.go:194

	}
	cryptoDir := filepath.Join(homeDir, ".cc-connect")
	if err := os.MkdirAll(cryptoDir, 0o700); err != nil {
		return nil, fmt.Errorf("create data dir: %w", err)
	}
	dbPath := filepath.Join(cryptoDir, fmt.Sprintf("matrix-crypto-%s.db", client.DeviceID))

	// Derive a stable pickle key from the access token
	h := sha256.Sum256([]byte(p.accessToken))
	pickleKey := make([]byte, 32)
	copy(pickleKey, h[:])

	return p.tryInitCrypto(ctx, client, pickleKey, dbPath, false)
}

func (p *Platform) tryInitCrypto(ctx context.Context, client *mautrix.Client, pickleKey []byte, dbPath string, isRetry bool) (*cryptohelper.CryptoHelper, error) {
	ch, err := cryptohelper.NewCryptoHelper(client, pickleKey, dbPath)
	if err != nil {
		return nil, fmt.Errorf("create crypto helper: %w", err)
	}
	ch.DBAccountID = client.UserID.String()

	if err := ch.Init(ctx); err != nil {
		if !isRetry && strings.Contains(err.Error(), "not marked as shared") {
			slog.Warn("matrix: stale device keys on server, force-uploading new keys")
			func() {
				defer func() { recover() }()
				if mach := ch.Machine(); mach != nil {
					if shareErr := mach.ShareKeys(ctx, -1); shareErr != nil {
						slog.Error("matrix: failed to force-share keys", "error", shareErr)
					}
				}
			}()
			ch.Close()
			client.StateStore = nil
			client.Store = mautrix.NewMemorySyncStore()
			return p.tryInitCrypto(ctx, client, pickleKey, dbPath, true)

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Ensure the directory containing dbPath exists and is writable by the process (mkdir -p and chown/chmod).
  2. Verify the dbPath scheme matches an available store backend (e.g. sqlite3 driver compiled in); add the driver import if missing.
  3. Delete a corrupted crypto store DB and restart to force fresh key upload (the code already handles 'not marked as shared' retries, but corruption may need manual cleanup).
  4. Regenerate the pickle key and restart the platform so a fresh helper is created.
  5. Check startup logs for the underlying wrapped error to identify the store-level cause.

Example fix

// before
ch, err := cryptohelper.NewCryptoHelper(client, pickleKey, "/var/lib/cc-connect/crypto.db")
// after
if err := os.MkdirAll("/var/lib/cc-connect", 0o755); err != nil {
    return nil, fmt.Errorf("prepare crypto db dir: %w", err)
}
ch, err := cryptohelper.NewCryptoHelper(client, pickleKey, "/var/lib/cc-connect/crypto.db")
Defensive patterns

Strategy: try-catch

Validate before calling

// Go: before starting the platform, ensure the crypto DB path is usable
if err := os.MkdirAll(filepath.Dir(dbPath), 0o755); err != nil {
    return fmt.Errorf("crypto db dir not creatable: %w", err)
}
if f, err := os.OpenFile(dbPath+".probe", os.O_CREATE|os.O_WRONLY, 0o600); err != nil {
    return fmt.Errorf("crypto db path not writable: %w", err)
} else { f.Close(); os.Remove(dbPath + ".probe") }

Try / catch

plat, err := matrix.New(opts)
if err != nil {
    var ctxErr error
    if errors.As(err, &ctxErr) && strings.Contains(err.Error(), "create crypto helper") {
        slog.Error("E2EE store setup failed — check crypto db path/permissions", "cause", errors.Unwrap(err))
    }
    return err
}

Prevention

When it happens

Trigger: tryInitCrypto calls cryptohelper.NewCryptoHelper(client, pickleKey, dbPath) and it returns err — e.g. the crypto store database at dbPath cannot be opened/created, the SQL driver is unavailable, the pickle key is invalid/empty in a way the store rejects, or the underlying mautrix client state is inconsistent.

Common situations: The data directory for the crypto DB does not exist or is not writable (read-only filesystem, wrong permissions, container volume not mounted); the configured DB path uses an unsupported sqlstore URI; the pickle key was generated with a broken RNG or reused/corrupted state from a previous run.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/888c4d3ab865b9ce. Report an issue: GitHub.