golang-migrate/migrate · error
failed to append PEM
Error message
failed to append PEM
What it means
ErrAppendPEM is returned by the ql/mysql config path when rootCertPool.AppendCertsFromPEM(pem) fails, i.e. the bytes read for the CA certificate were not a valid PEM-encoded certificate. The driver cannot build a TLS trust pool from the provided file or inline value, so it refuses to configure a secure connection rather than silently trusting nothing.
Source
Thrown at database/ql/ql.go:26
nurl "net/url"
"strings"
"sync/atomic"
"github.com/golang-migrate/migrate/v4"
"github.com/golang-migrate/migrate/v4/database"
_ "modernc.org/ql/driver"
)
func init() {
database.Register("ql", &Ql{})
}
var DefaultMigrationsTable = "schema_migrations"
var (
ErrDatabaseDirty = fmt.Errorf("database is dirty")
ErrNilConfig = fmt.Errorf("no config")
ErrNoDatabaseName = fmt.Errorf("no database name")
ErrAppendPEM = fmt.Errorf("failed to append PEM")
)
type Config struct {
MigrationsTable string
DatabaseName string
}
type Ql struct {
db *sql.DB
isLocked atomic.Bool
config *Config
}
func WithInstance(instance *sql.DB, config *Config) (database.Driver, error) {
if config == nil {
return nil, ErrNilConfig
}View on GitHub (pinned to 01a9643f14)
Solutions
- Convert the certificate to PEM format (openssl x509 -inform DER -in cert.der -out cert.pem) and re-run
- Verify the file exists, is readable by the process, and starts with '-----BEGIN CERTIFICATE-----'
- Download the CA bundle again and confirm the checksum; ensure the mounted secret contains the cert, not an error page
Example fix
// before
ca, _ := os.ReadFile("cert.der") // DER-encoded
// after
ca, _ := os.ReadFile("cert.pem") // PEM: "-----BEGIN CERTIFICATE-----"
if !strings.Contains(string(ca), "BEGIN CERTIFICATE") {
return errors.New("CA file is not PEM-encoded")
} Defensive patterns
Strategy: validation
Validate before calling
func validateCAFile(path string) error {
pem, err := os.ReadFile(path)
if err != nil {
return err
}
if !bytes.Contains(pem, []byte("-----BEGIN CERTIFICATE-----")) {
return fmt.Errorf("%s is not PEM-encoded", path)
}
return nil
} Try / catch
if err := validateCAFile(caPath); err != nil {
return fmt.Errorf("invalid TLS CA: %w", err)
}
// otherwise connect and handle ErrAppendPEM explicitly:
if errors.Is(err, mysql.ErrAppendPEM) {
return fmt.Errorf("CA file %s is not PEM-encoded", caPath)
} Prevention
- Always distribute certificates in PEM (Base64) format, not DER
- Verify mounted secrets actually contain cert bytes and start with -----BEGIN CERTIFICATE-----
- Run a TLS preflight check (openssl x509 -in cert.pem -noout) in CI/deploy hooks
When it happens
Trigger: Providing x-tls-ca / cert path whose file content is not PEM (DER binary, HTML error page, empty file, truncated download); the same sentinel is returned by mysql's urlToMySQLConfig when the x-tls-ca file's bytes fail AppendCertsFromPEM.
Common situations: Pointing x-tls-ca at a DER-encoded certificate instead of PEM; an expired/rotated cert file replaced by a proxy error page; forgetting to mount the secret in Kubernetes so the file is empty; concatenating keys into the CA file incorrectly.
Related errors
- failed to append PEM
- to use TLS client authentication, both x-tls-cert and x-tls-
- no config
- no keyspace provided
- no config
AI-assisted analysis of golang-migrate/migrate@01a9643f14 (2026-09-02).
Data as JSON: /api/errors/344605b3981974bb.
Report an issue: GitHub.