chenhg5/cc-connect · error

weixin: create state dir: %w

Error message

weixin: create state dir: %w

What it means

New creates the persistence directory given by the "state_dir" option using os.MkdirAll. If directory creation fails (permissions, read-only filesystem, path is a file), the error is wrapped and platform construction fails.

Source

Thrown at platform/weixin/weixin.go:274

		allowFrom:       allowFrom,
		routeTag:        routeTag,
		stateDir:        stateDir,
		longPollMS:      lp,
		accountLabel:    accountLabel,
		httpClient:      httpClient,
		cdnHttpClient:   cdnHttpClient,
		tokens:          make(map[string]string),
		dedupEnabled:    dedupEnabled,
		dedup:           core.NewMessageDedup(time.Duration(dedupWindow) * time.Second),
		typingTickets:   make(map[string]typingTicketEntry),
		sendQuotaLimit:  burstLimit,
		sendQuotaWindow: time.Duration(burstWindow) * time.Second,
	}
	p.api = newAPIClient(baseURL, token, routeTag, httpClient)

	if stateDir != "" {
		if err := os.MkdirAll(stateDir, 0o755); err != nil {
			return nil, fmt.Errorf("weixin: create state dir: %w", err)
		}
		p.syncBufPath = filepath.Join(stateDir, "get_updates.buf")
		p.tokensPath = filepath.Join(stateDir, "context_tokens.json")
		p.loadSyncBuf()
		p.loadTokens()
	}

	return p, nil
}

func pickInt(v any) int {
	switch x := v.(type) {
	case int:
		return x
	case int64:
		return int(x)
	case float64:
		return int(x)

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Fix filesystem permissions on the state_dir parent or run as a user with write access
  2. Ensure state_dir is not an existing regular file; remove/rename it
  3. Point state_dir to a writable location (e.g. /var/lib/cc-connect or a volume)
  4. Check the wrapped inner error (ENOENT/EACCES/ENOTDIR) for the exact cause

Example fix

// before
"state_dir": "/etc/cc-connect/state"   // root-owned, read-only for the bot user
// after
"state_dir": "/var/lib/cc-connect/weixin-state"   // owned by the bot user
Defensive patterns

Strategy: validation

Validate before calling

if sd, _ := opts["state_dir"].(string); sd != "" {
    if fi, err := os.Stat(sd); err == nil && !fi.IsDir() {
        return fmt.Errorf("state_dir %s is not a directory", sd)
    }
    if err := os.MkdirAll(sd, 0o755); err != nil {
        return fmt.Errorf("cannot create state_dir %s: %w", sd, err)
    }
}

Try / catch

p, err := weixin.New(opts)
if err != nil && strings.Contains(err.Error(), "create state dir") {
    log.Error("state dir unwritable; check permissions/mount", "err", err)
    return err
}

Prevention

When it happens

Trigger: weixin.New with a non-empty state_dir that cannot be created: parent directories not writable, the path exists as a regular file, disk full, or SELinux/container read-only mount.

Common situations: Running in a read-only container filesystem with default state dir; state_dir points into a root-owned directory while the bot runs unprivileged; a stale file occupies the state_dir path; typo in path so MkdirAll tries to create dirs under an invalid parent.

Understand the failure class

Background: mkdir permission denied (EACCES): failed to create directory errors explained — this error's family across 32 libraries.

Related errors


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