sipeed/picoclaw · error
failed to save config: %w
Error message
failed to save config: %w
What it means
config.SaveConfig failed after a successful WeCom QR login, meaning the bot credentials (BotID + encrypted Secret) were obtained but NOT persisted. SaveConfig first writes a .security.yml alongside the config, then marshals and atomically writes config.json (pkg/config/config.go:1672-1700), so failure can come from either file: marshal error, unwritable directory, or atomic rename failure.
Source
Thrown at cmd/picoclaw/internal/auth/wecom.go:130
}
cfg, err := internal.LoadConfig()
if err != nil {
return fmt.Errorf("failed to load config: %w", err)
}
opts := defaultWeComQRFlowOptions(timeout)
opts.Writer = writer
botInfo, err := scanner(ctx, opts)
if err != nil {
return err
}
applyWeComAuthResult(cfg, botInfo)
if saveErr := config.SaveConfig(internal.GetConfigPath(), cfg); saveErr != nil {
return fmt.Errorf("failed to save config: %w", saveErr)
}
fmt.Fprintln(writer)
fmt.Fprintln(writer, "WeCom connected.")
fmt.Fprintf(writer, "Bot ID: %s\n", botInfo.BotID)
fmt.Fprintf(writer, "Config: %s\n", internal.GetConfigPath())
return nil
}
func defaultWeComQRFlowOptions(timeout time.Duration) wecomQRFlowOptions {
if timeout <= 0 {
timeout = wecomQRPollTimeout
}
return wecomQRFlowOptions{
HTTPClient: &http.Client{Timeout: wecomQRHTTPTimeout},
GenerateURL: wecomQRGenerateEndpoint,View on GitHub (pinned to 49183d7e8d)
Solutions
- Fix ownership of the config directory: chown -R $(whoami) ~/.picoclaw (or the PICOCLAW_CONFIG_PATH directory)
- Check disk space (df -h) and inodes (df -i)
- Close other running picoclaw processes that may hold the config, then re-run login
- Verify PICOCLAW_CONFIG_PATH/PICOCLAW_HOME point to a writable location
- Re-run the WeCom login after fixing writes — the credentials are only persisted on a successful save, so the bot must be re-linked
Defensive patterns
Strategy: fallback
Validate before calling
// Pre-flight writability check before an interactive login
p := internal.GetConfigPath()
f, err := os.OpenFile(p, os.O_WRONLY|os.O_CREATE, 0o600)
if err != nil {
return fmt.Errorf("config %s is not writable: %w", p, err)
}
f.Close() Type guard
func isPermissionErr(err error) bool {
return err != nil && (os.IsPermission(err) || errors.Is(err, fs.ErrPermission))
} Try / catch
if saveErr := config.SaveConfig(internal.GetConfigPath(), cfg); saveErr != nil {
if isPermissionErr(saveErr) {
fmt.Fprintf(writer, "Login succeeded but config could not be saved. Fix permissions on %s and re-run.\nBot ID: %s\n", internal.GetConfigPath(), botInfo.BotID)
}
return fmt.Errorf("failed to save config: %w", saveErr)
} Prevention
- Ensure ~/.picoclaw (and PICOCLAW_CONFIG_PATH target) is user-writable before login flows
- Run only one picoclaw instance when credentials may be written
- Monitor disk space; atomic writes fail on full disks
When it happens
Trigger: ~/.picoclaw directory or config.json owned by another user/root; disk full; PICOCLAW_CONFIG_PATH set to a read-only location; .security.yml locked by a concurrently running picoclaw process; invalid config state that cannot marshal.
Common situations: Running the login under sudo previously so files are root-owned; disk quota exhausted; two picoclaw instances writing config simultaneously; SELinux/AppArmor denying writes to the home dot-directory.
Related errors
- failed to read security config: %w
- create output dir: %w
- failed to load config: %w
- invalid WeCom QR generate URL: %w
- invalid WeCom QR query URL: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/3cf5795613b80d7b.
Report an issue: GitHub.