sipeed/picoclaw · critical

open whatsapp store: %w

Error message

open whatsapp store: %w

What it means

Returned by WhatsAppNativeChannel.Start when sql.Open(sqliteDriver, connStr) fails for the session database 'file:<storePath>/store.db?_foreign_keys=on'. Unlike most sql drivers, open errors here mean the driver name is not registered or the DSN is rejected immediately - sql.Open itself does not touch the file. The %w preserves the database/sql error.

Source

Thrown at pkg/channels/whatsapp_native/whatsapp_native.go:105

	// Reset lifecycle state from any previous Stop() so a restarted channel
	// behaves correctly.  Use reconnectMu to be consistent with eventHandler
	// and Stop() which coordinate under the same lock.
	c.reconnectMu.Lock()
	c.stopping.Store(false)
	c.reconnecting = false
	c.reconnectMu.Unlock()

	if err := os.MkdirAll(c.storePath, 0o700); err != nil {
		return fmt.Errorf("create session store dir: %w", err)
	}

	dbPath := filepath.Join(c.storePath, whatsappDBName)
	connStr := "file:" + dbPath + "?_foreign_keys=on"

	db, err := sql.Open(sqliteDriver, connStr)
	if err != nil {
		return fmt.Errorf("open whatsapp store: %w", err)
	}
	db.SetMaxOpenConns(1)
	db.SetMaxIdleConns(1)
	if _, err = db.ExecContext(ctx, "PRAGMA foreign_keys = ON"); err != nil {
		_ = db.Close()
		return fmt.Errorf("enable foreign keys: %w", err)
	}

	waLogger := waLog.Stdout("WhatsApp", "WARN", true)
	container := sqlstore.NewWithDB(db, sqliteDriver, waLogger)
	if err = container.Upgrade(ctx); err != nil {
		_ = db.Close()
		return fmt.Errorf("open whatsapp store: %w", err)
	}

	deviceStore, err := container.GetFirstDevice(ctx)
	if err != nil {
		_ = container.Close()

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Rebuild with the same build tags used for the sqlite driver import (whatsapp_native tag set) so the driver registers.
  2. Confirm CGO settings match the chosen sqlite driver (CGO_ENABLED=1 for mattn/go-sqlite3, or use the pure-Go driver).
  3. Print/inspect the effective storePath to rule out DSN-breaking characters.
  4. Check sqliteDriver constant in the package matches the driver name registered by the imported library.
Defensive patterns

Strategy: validation

Validate before calling

// Verify the sqlite driver is linked into this build before Start:
var found bool
for _, d := range sql.Drivers() {
    if d == "sqlite" { found = true }
}
if !found {
    return errors.New("sqlite driver not compiled in; rebuild with the required build tags")
}

Try / catch

if err := ch.Start(ctx); err != nil {
    if strings.Contains(err.Error(), "open whatsapp store") {
        // distinguish sql.Open failure (driver/DSN) from later upgrade failure via unwrap chain
    }
}

Prevention

When it happens

Trigger: Start(ctx) builds the DSN from storePath and calls sql.Open; failure occurs when the sqlite driver (CGO or pure-Go, depending on build tags) was not linked into the binary, or the DSN is malformed for the registered driver (e.g. unsupported '_foreign_keys' query parameter).

Common situations: Building with a driver variant that registers a different driver name (e.g. 'sqlite3' vs 'sqlite'); CGO disabled causing the CGO sqlite driver to be excluded; a build-tag mismatch between the driver import and whatsapp_native code; storePath containing characters the DSN parser rejects.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/7cbbdcc986452d0e. Report an issue: GitHub.