gofiber/fiber · error

cannot chmod %#o for %q: %w

Error message

cannot chmod %#o for %q: %w

What it means

Raised by createListener after a unix-domain listener is successfully created but os.Chmod of the socket path to cfg.UnixSocketFileMode fails. The chmod is intentional hardening so only the intended users can connect to the socket; on failure Fiber closes the listener and returns the error rather than leaving the socket with default (often world-readable) permissions. The %#o prints the requested octal mode in the message.

Source

Thrown at listen.go:392

		}
	}

	if tlsConfig != nil {
		listener, err = tls.Listen(cfg.ListenerNetwork, addr, tlsConfig)
	} else {
		listener, err = net.Listen(cfg.ListenerNetwork, addr)
	}

	// Check for error before using the listener
	if err != nil {
		// Wrap the error from tls.Listen/net.Listen
		return nil, fmt.Errorf("failed to listen: %w", err)
	}

	if cfg.ListenerNetwork == NetworkUnix {
		if err = os.Chmod(addr, cfg.UnixSocketFileMode); err != nil {
			_ = listener.Close() //nolint:errcheck // best-effort cleanup on the error path
			return nil, fmt.Errorf("cannot chmod %#o for %q: %w", cfg.UnixSocketFileMode, addr, err)
		}
	}

	if cfg.ListenerAddrFunc != nil {
		cfg.ListenerAddrFunc(listener.Addr())
	}

	return listener, nil
}

func (app *App) printMessages(cfg *ListenConfig, listenData *ListenData) {
	app.startupMessage(listenData, cfg)

	if cfg.EnablePrintRoutes {
		app.printRoutesMessage()
	}
}

View on GitHub (pinned to 9a4c7e57fe)

Solutions

  1. Ensure the process owns (or can write) both the socket file and its parent directory.
  2. Place the socket in a directory the process fully controls, e.g. /run/user/<uid>/ or a dedicated /var/lib/<app> dir.
  3. Disable or adjust any cron/systemd unit that aggressively cleans the socket directory.
  4. If SELinux/AppArmor is involved, allow chmod on the socket path's type in the policy.
  5. Verify cfg.UnixSocketFileMode is a sane octal like 0o660 (avoid unset/zero).

Example fix

// before: binding in a root-owned dir, chmod 0660 fails for non-root
app.Listen("/var/run/app.sock", fiber.ListenConfig{
  ListenerNetwork:    fiber.NetworkUnix,
  UnixSocketFileMode: 0o660,
})

// after: use a writable per-app dir
os.MkdirAll("/var/lib/app", 0o755)
app.Listen("/var/lib/app/app.sock", fiber.ListenConfig{
  ListenerNetwork:    fiber.NetworkUnix,
  UnixSocketFileMode: 0o660,
})
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the socket's parent dir is writable by this process before listening.
func ensureWritableSocketDir(path string) error {
    dir := filepath.Dir(path)
    if err := os.MkdirAll(dir, 0o755); err != nil {
        return fmt.Errorf("mkdir %q: %w", dir, err)
    }
    probe := filepath.Join(dir, ".writeprobe")
    f, err := os.Create(probe)
    if err != nil {
        return fmt.Errorf("dir %q not writable: %w", dir, err)
    }
    _ = f.Close()
    _ = os.Remove(probe)
    return nil
}

Type guard

null

Try / catch

err := app.Listen(sock, fiber.ListenConfig{
    ListenerNetwork:    fiber.NetworkUnix,
    UnixSocketFileMode: 0o660,
})
if err != nil && strings.Contains(err.Error(), "cannot chmod") {
    log.Errorf("post-listen chmod failed — check parent dir perms: %v", err)
}

Prevention

When it happens

Trigger: ListenerNetwork is unix, the socket was bound (e.g. under /var/run), but chmod fails because: the parent dir lacks write permission for the current user, the socket was already removed by a concurrent cleanup, SELinux/AppArmor denies chmod on the type, or cfg.UnixSocketFileMode is a nonsensical value the syscall rejects.

Common situations: Running the process under a uid that can create but not chmod a file in /var/run (root-owned dir); a watchdog concurrently deletes idle sockets; container with a read-only /run; passing a mode constant that isn't valid octal.

Related errors


AI-assisted analysis of gofiber/fiber@9a4c7e57fe (2026-08-04). Data as JSON: /data/errors/a3c2a416de6d61dc.json. Report an issue: GitHub.