sipeed/picoclaw · critical
create session store dir: %w
Error message
create session store dir: %w
What it means
Returned by WhatsAppNativeChannel.Start when os.MkdirAll fails to create the whatsmeow session store directory (c.storePath) with mode 0700. The %w chain keeps the OS error (e.g. permission denied, path component is a file, read-only filesystem). Start aborts before opening the sqlite session database.
Source
Thrown at pkg/channels/whatsapp_native/whatsapp_native.go:97
config: cfg,
storePath: storePath,
}
return c, nil
}
func (c *WhatsAppNativeChannel) Start(ctx context.Context) error {
logger.InfoCF("whatsapp", "Starting WhatsApp native channel (whatsmeow)", map[string]any{"store": c.storePath})
// 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)View on GitHub (pinned to 49183d7e8d)
Solutions
- Check the store path in channel config and ensure every parent directory exists and is writable by the service user.
- Create the directory manually with correct ownership: install -d -o <user> -m 700 <storePath>.
- If running in Docker, confirm the volume is mounted rw at the configured path.
- Look for a regular file shadowing a directory component (e.g. a file named like the dir) and remove/rename it.
Defensive patterns
Strategy: validation
Validate before calling
info, err := os.Stat(storePath)
switch {
case err == nil && !info.IsDir():
return fmt.Errorf("store path %s exists and is not a directory", storePath)
case os.IsNotExist(err):
if err := os.MkdirAll(storePath, 0o700); err != nil { return err }
case err != nil:
return err
} Try / catch
if err := ch.Start(ctx); err != nil {
if errors.Is(err, fs.ErrPermission) || strings.Contains(err.Error(), "create session store dir") {
// fix directory ownership/permissions, then retry Start
}
} Prevention
- Create and chown the store directory as part of deployment (systemd StateDirectory, Docker volume, etc.).
- Smoke-test storePath writability with a temp file before enabling the channel.
- Never run the service once as root and then as an unprivileged user against the same store.
When it happens
Trigger: Start(ctx) with storePath whose parent lacks write permission, a path where an existing regular file occupies a needed directory component, a read-only volume, or SELinux/AppArmor denial.
Common situations: Running the binary as an unprivileged user against /var/lib/... or another root-owned path; container with a read-only or unmounted volume at the store path; store path misconfigured to something like /proc/... or a path containing a stray file; macOS/linux permission mismatch after moving the data dir.
Related errors
- error in open panic: %v
- error initializing panic log: %v
- create output dir: %w
- failed to save config: %w
- failed to save config: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/8c3d22fbaa0ab0f8.
Report an issue: GitHub.