{"record":{"id":"9891dd85edc6b803","repo":"AlistGo/alist","slug":"signing-failed-v","errorCode":null,"errorMessage":"signing failed: %v","messagePattern":"signing failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/github/util.go","lineNumber":154,"sourceCode":"\tfor _, p := range parents {\n\t\tcommit.WriteString(fmt.Sprintf(\"parent %s\\n\", p))\n\t}\n\tnow := time.Now()\n\t_, offset := now.Zone()\n\thour := offset / 3600\n\tauthor := (*m)[\"author\"].(map[string]string)\n\tcommit.WriteString(fmt.Sprintf(\"author %s <%s> %d %+03d00\\n\", author[\"name\"], author[\"email\"], now.Unix(), hour))\n\tauthor[\"date\"] = now.Format(time.RFC3339)\n\tcommitter := (*m)[\"committer\"].(map[string]string)\n\tcommit.WriteString(fmt.Sprintf(\"committer %s <%s> %d %+03d00\\n\", committer[\"name\"], committer[\"email\"], now.Unix(), hour))\n\tcommitter[\"date\"] = now.Format(time.RFC3339)\n\tcommit.WriteString(fmt.Sprintf(\"\\n%s\", (*m)[\"message\"].(string)))\n\tdata := commit.String()\n\n\tvar sigBuffer bytes.Buffer\n\terr := openpgp.DetachSign(&sigBuffer, entity, strings.NewReader(data), nil)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"signing failed: %v\", err)\n\t}\n\tvar armoredSig bytes.Buffer\n\tarmorWriter, err := armor.Encode(&armoredSig, \"PGP SIGNATURE\", nil)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif _, err = utils.CopyWithBuffer(armorWriter, &sigBuffer); err != nil {\n\t\treturn \"\", err\n\t}\n\t_ = armorWriter.Close()\n\treturn armoredSig.String(), nil\n}\n","sourceCodeStart":136,"sourceCodeEnd":167,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/github/util.go#L136-L167","documentation":"Thrown by the GitHub driver when creating a release commit signature: openpgp.DetachSign fails to produce a detached PGP signature over the commit payload. The signing entity is derived from the driver's configured GPG private key, so failure almost always means the key could not be parsed, is encrypted with a passphrase (which x/crypto/openpgp cannot unlock here since nil config is passed), or has no private-key material. It surfaces as 'signing failed: <openpgp error>'.","triggerScenarios":"Calling the GitHub driver's release-creation path that signs a commit (MakeDir/commit building for github_releases with a signing key configured). DetachSign is invoked with the entity obtained from the configured private key; any armored-key parse error, missing private key ring, or passphrase-protected key triggers this before armoring begins.","commonSituations":"User pastes a GPG public key instead of the private key into driver config; key is exported with --armor but has a passphrase; key uses unsupported algorithm (e.g. newer Ed25519 with unsupported subkey packets in older x/crypto/openpgp); whitespace/newline corruption when copying the armored key into the admin UI.","solutions":["Re-export the private key without a passphrase: gpg --export-secret-keys --armor KEYID and paste the full ARMORED block including headers","Verify the key parses standalone: gpg --list-packets key.asc or try signing with gpg --detach-sign locally","Check the driver's key parsing code path (entity creation from config) and its error, since the %v in 'signing failed' carries the exact openpgp reason","If a passphrase is mandatory, decrypt first: gpg --export-options export-reset-subkey-passwd --export-secret-keys, or use a key without passphrase"],"exampleFix":"// before: passphrase-protected or public key in config\nd.Addition.PrivateKey = publicKeyArmored // DetachSign fails: signing failed: openpgp: ...\n\n// after: export unencrypted private key\nd.Addition.PrivateKey = privateKeyArmored // gpg --export-secret-keys --armor KEYID","handlingStrategy":"validation","validationCode":"// Validate the armored private key parses and holds a private entity before Init\nimport (\n  \"crypto/openpgp\"\n  \"strings\"\n)\nfunc validSigningKey(armored string) bool {\n  if armored == \"\" { return false }\n  entityList, err := openpgp.ReadArmoredKeyRing(strings.NewReader(armored))\n  return err == nil && len(entityList) == 1 && entityList[0].PrivateKey != nil && !entityList[0].PrivateKey.Encrypted\n}","typeGuard":null,"tryCatchPattern":"err := driver.Init(ctx)\nif err != nil {\n  if strings.Contains(err.Error(), \"signing failed\") {\n    // key material problem: fix config, do not retry\n  }\n}","preventionTips":["Store the GPG private key (not public) in driver config, exported without a passphrase","Test signing locally with gpg --detach-sign before pasting the key","Keep the full ARMOR BEGIN/END block intact when copying"],"tags":["github","gpg","signing","openpgp","release"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}