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

  1. Convert the certificate to PEM format (openssl x509 -inform DER -in cert.der -out cert.pem) and re-run
  2. Verify the file exists, is readable by the process, and starts with '-----BEGIN CERTIFICATE-----'
  3. 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

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


AI-assisted analysis of golang-migrate/migrate@01a9643f14 (2026-09-02). Data as JSON: /api/errors/344605b3981974bb. Report an issue: GitHub.