OpenNHP/opennhp · critical

fail to write private key file

Error message

fail to write private key file: %w

What it means

generateCosignKeyPair writes the cosign private key bytes to privateKeyPath with mode 0600 after creating its directory. This error is returned when os.WriteFile fails (wrapped as 'fail to write private key file: %w').

Solutions

  1. Check the wrapped error for EACCES/ENOSPC/EISDIR and correct permissions or free disk space
  2. Ensure privateKeyPath is a writable file path, not an existing directory
  3. Run the process as a user owning the key directory
  4. Mount the key directory as a writable volume in container deployments
Defensive patterns

Strategy: try-catch

Validate before calling

if fi, err := os.Stat(privateKeyPath); err == nil && fi.IsDir() {
	return fmt.Errorf("%s is a directory", privateKeyPath)
}
if err := syscall.Access(filepath.Dir(privateKeyPath), syscall.W_OK); err != nil {
	return fmt.Errorf("key dir not writable: %w", err)
}

Try / catch

if err := generateCosignKeyPair(); err != nil {
	if strings.Contains(err.Error(), "fail to write private key file") {
		log.Fatalf("private key write failed (check disk space/permissions): %v", err)
	}
	log.Fatalf("cosign keypair init failed: %v", err)
}

Prevention

When it happens

Trigger: At init, writing the private key fails — directory exists but is not writable, disk full, path is an existing directory, or quota exceeded.

Common situations: Disk-full on the node hosting nhp-server; privateKeyPath colliding with an existing directory name; permissions changed after first run; read-only overlay during container upgrades.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07). Data as JSON: /api/errors/92ed6b610c4b8627. Report an issue: GitHub.

Appendix: source

Thrown at endpoints/server/kbs/resource/resource.go:65

			return nil
		}
	}

	keys, err := cosign.GenerateKeyPair(nil)
	if err != nil {
		return err
	}

	if err := os.MkdirAll(filepath.Dir(privateKeyPath), 0755); err != nil {
		return fmt.Errorf("fail to create private key directory: %w", err)
	}

	if err := os.MkdirAll(filepath.Dir(publicKeyPath), 0755); err != nil {
		return fmt.Errorf("fail to create public key directory: %w", err)
	}

	if err := os.WriteFile(privateKeyPath, keys.PrivateBytes, 0600); err != nil {
		return fmt.Errorf("fail to write private key file: %w", err)
	}

	if err := os.WriteFile(publicKeyPath, keys.PublicBytes, 0644); err != nil { //nolint:gosec // G306: Public keys are intentionally world-readable
		return fmt.Errorf("fail to write public key file: %w", err)
	}

	return nil
}

func GetResource(c *gin.Context) {
	path := c.Param("path")
	if path == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "resource path is empty"})
		return
	}

	authHeader := c.GetHeader("Authorization")
	if authHeader == "" || !strings.HasPrefix(authHeader, "Bearer ") {

View on GitHub (pinned to 6e04ca5ff0)