netbirdio/netbird · error
sign artifact: %w
Error message
sign artifact: %w
What it means
reposign.SignData refused to sign. Two concrete refusals exist in reposign/artifact.go:267-281: an empty artifact ("artifact length must be positive, got 0") and an expired artifact key ("artifact key expired at ...", checked against the ExpiresAt embedded in the key). A BLAKE2s hash write error is theoretically possible but practically never occurs.
Source
Thrown at client/cmd/signer/artifactsign.go:146
return fmt.Errorf("read private key file: %w", err)
}
} else {
return fmt.Errorf("artifact private key must be provided via %s environment variable or --artifact-key-file flag", envArtifactPrivateKey)
}
privateKey, err := reposign.ParseArtifactKey(privKeyPEM)
if err != nil {
return fmt.Errorf("failed to parse artifact private key: %w", err)
}
artifactData, err := os.ReadFile(artifactFile)
if err != nil {
return fmt.Errorf("read artifact file: %w", err)
}
signature, err := reposign.SignData(privateKey, artifactData)
if err != nil {
return fmt.Errorf("sign artifact: %w", err)
}
sigFile := artifactFile + ".sig"
if err := os.WriteFile(artifactFile+".sig", signature, 0o600); err != nil {
return fmt.Errorf("write signature file (%s): %w", sigFile, err)
}
cmd.Printf("✅ Artifact signed successfully.\n")
cmd.Printf("Signature file: %s\n", sigFile)
return nil
}
func handleVerifyArtifact(cmd *cobra.Command, pubKeyFile, artifactFile, signatureFile string) error {
cmd.Println("🔍 Verifying artifact...")
// Read artifact public key
pubKeyPEM, err := os.ReadFile(pubKeyFile)
if err != nil {View on GitHub (pinned to 93e97f4bf1)
Solutions
- Check the artifact is non-empty: stat -c %s <file>; rebuild if it is 0 bytes
- Inspect the key's ExpiresAt (printed by generate-artifact-key / visible in the PEM JSON); if expired, generate a fresh artifact key and publish its root-signed public bundle
- Verify system time with timedatectl or ntp status before signing
- If the key was regenerated, make sure downstream verifiers fetch the new root-signed artifact-key-pub.pem
Example fix
// before
signature, err := reposign.SignData(privateKey, artifactData)
if err != nil {
return fmt.Errorf("sign artifact: %w", err)
}
// after
if len(artifactData) == 0 {
return fmt.Errorf("artifact %s is empty", artifactFile)
}
if !privateKey.Metadata.ExpiresAt.IsZero() && time.Now().After(privateKey.Metadata.ExpiresAt) {
return fmt.Errorf("artifact key expired at %v", privateKey.Metadata.ExpiresAt)
}
signature, err := reposign.SignData(privateKey, artifactData)
if err != nil {
return fmt.Errorf("sign artifact: %w", err)
} Defensive patterns
Strategy: validation
Validate before calling
info, err := os.Stat(artifactFile)
if err != nil || info.Size() == 0 {
log.Fatalf("artifact %s must exist and be non-empty", artifactFile)
} Prevention
- Record each artifact key's ExpiresAt in the release runbook and rotate before it lapses
- Add a pre-sign check that the file size is positive
- Keep NTP enabled on signing hosts so expiry checks are not skewed
When it happens
Trigger: --artifact-file points at a 0-byte file (touched placeholder or failed build); the artifact key's embedded ExpiresAt is in the past; significant clock skew on the signing host.
Common situations: Pipeline signs a placeholder file created by touch; reusing an artifact key past the expiration window chosen at generate-artifact-key time; host clock drift.
Related errors
- failed to parse artifact private key: %w
- failed to parse artifact public key: %w
- artifact verification failed: %w
- failed to parse root public key(s): %w
- artifact key verification failed: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/7f775caab1e16109.
Report an issue: GitHub.