juicedata/juicefs · critical

unable to create engine: %s

Error message

unable to create engine: %s

What it means

JuiceFS builds a MySQL DSN (forcing transaction_isolation to repeatable-read) and calls xorm.NewEngine. If the driver cannot create an engine from the DSN (malformed DSN, bad params), this error wraps the underlying message. It is thrown before any network activity, so it almost always means the connection string itself is invalid.

Source

Thrown at pkg/meta/sql_mysql.go:67

	}
	return addr
}

func createMySQLEngine(dsn string) (*xorm.Engine, error) {
	cfg, err := mysql.ParseDSN(recoveryMysqlPwd(dsn))
	if err != nil {
		return nil, err
	}
	if cfg.Params == nil {
		cfg.Params = make(map[string]string)
	}

	var engine *xorm.Engine
	for _, key := range []string{"transaction_isolation", "tx_isolation"} {
		cfg.Params[key] = "'repeatable-read'"
		engine, err = xorm.NewEngine("mysql", cfg.FormatDSN())
		if err != nil {
			return nil, fmt.Errorf("unable to create engine: %s", err)
		}

		if err = engine.Ping(); err == nil {
			return engine, nil
		}

		_ = engine.Close()
		delete(cfg.Params, key)

		if !isUnknownTransactionIsolationErr(err, key) {
			return nil, fmt.Errorf("ping database: %s", err)
		}
	}

	return nil, fmt.Errorf("failed to set isolation level: %s", err)
}

func isUnknownTransactionIsolationErr(err error, key string) bool {

View on GitHub (pinned to c9a67b23e8)

Solutions

  1. Check the inner driver message in the error for the exact DSN problem and fix the meta URL accordingly.
  2. URL-encode the password (and any special chars) in the meta URL, or pass the DSN with the password omitted and use env/my.cnf.
  3. Verify the DSN follows go-sql-driver format: user:password@tcp(host:port)/dbname?params.
  4. Test the same DSN with a small Go/CLI program using go-sql-driver to isolate whether it is a JuiceFS or driver issue.

Example fix

// before (bad DSN: special chars unescaped)
juicefs format "mysql://root:p@ss!@tcp(1.2.3.4:3306)/jfs" vol
// after (URL-encode password: p@ss! -> p%40ss%21)
juicefs format "mysql://root:p%40ss%21@tcp(1.2.3.4:3306)/jfs" vol
Defensive patterns

Strategy: validation

Validate before calling

u, err := url.Parse(metaURL)
if err != nil || !strings.HasPrefix(u.Scheme, "mysql") {
    return fmt.Errorf("meta URL must be mysql://user:pass@tcp(host:port)/db")
}
if pw, ok := u.User.Password(); ok && url.QueryEscape(pw) != pw {
    return fmt.Errorf("password contains special characters: URL-encode it")
}

Try / catch

if err := runMount(); err != nil && strings.Contains(err.Error(), "unable to create engine") {
    log.Fatalf("invalid MySQL DSN: %v", err)
}

Prevention

When it happens

Trigger: `juicefs format mysql://...` / `mount mysql://...` with a DSN whose FormatDSN() output the go-sql-driver rejects — e.g. malformed `dsn` param, unescaped special characters in password, or invalid parameter syntax.

Common situations: Special characters (!@#) in the MySQL password not URL-encoded in the JuiceFS meta URL; typos like `mysql://user:pass@tcp(host:3306)/db` with missing tcp() wrapper; unsupported query params passed through.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06). Data as JSON: /api/errors/79fb965be6a355ce. Report an issue: GitHub.