golang-migrate/migrate · error
unmarshaling json error: %s
Error message
unmarshaling json error: %s
What it means
Raised in Mongo.Run when the migration file's contents fail to parse: bson.UnmarshalExtJSON cannot decode the migration JSON into a list of MongoDB commands ([]bson.D). The error wraps the underlying decoder message. It means the migration file is not valid Extended JSON or its top-level structure is not an array of command documents.
Source
Thrown at database/mongodb/mongodb.go:251
switch {
case err == mongo.ErrNoDocuments:
return database.NilVersion, false, nil
case err != nil:
return 0, false, &database.Error{OrigErr: err, Err: "failed to get migration version"}
default:
return versionInfo.Version, versionInfo.Dirty, nil
}
}
func (m *Mongo) Run(migration io.Reader) error {
migr, err := io.ReadAll(migration)
if err != nil {
return err
}
var cmds []bson.D
err = bson.UnmarshalExtJSON(migr, true, &cmds)
if err != nil {
return fmt.Errorf("unmarshaling json error: %s", err)
}
if m.config.TransactionMode {
if err := m.executeCommandsWithTransaction(context.TODO(), cmds); err != nil {
return err
}
} else {
if err := m.executeCommands(context.TODO(), cmds); err != nil {
return err
}
}
return nil
}
func (m *Mongo) executeCommandsWithTransaction(ctx context.Context, cmds []bson.D) error {
err := m.db.Client().UseSession(ctx, func(sessionContext mongo.SessionContext) error {
if err := sessionContext.StartTransaction(); err != nil {
return &database.Error{OrigErr: err, Err: "failed to start transaction"}
}View on GitHub (pinned to 01a9643f14)
Solutions
- Fix the JSON syntax reported by the wrapped unmarshal error
- Ensure the file is an array of command documents: [{"insert": ...}, ...]
- Use MongoDB Extended JSON (relaxed or canonical) for dates/ObjectIds: {"$date": "..."}, {"$oid": "..."}
- Save the file as plain UTF-8 without BOM
Example fix
// before (migrations/1_create.json)
{ "insertOne": { "document": { "createdAt": new Date() } } }
// after
[ { "insertOne": { "document": { "createdAt": { "$date": "2024-01-01T00:00:00Z" } } } } ] Defensive patterns
Strategy: validation
Validate before calling
var probe []map[string]any
if err := json.Unmarshal(raw, &probe); err != nil {
return fmt.Errorf("migration %s is not valid JSON: %w", path, err)
}
if _, ok := raw[0].([]any); !ok {
return fmt.Errorf("migration %s top level must be an array of commands", path)
} Try / catch
if err := m.Up(); err != nil {
if strings.Contains(err.Error(), "unmarshaling json error") {
return fmt.Errorf("fix Extended JSON in the failed .json migration: %w", err)
}
return err
} Prevention
- Validate migration JSON in CI with a strict Extended JSON parser
- Use {"$date": ...} and {"$oid": ...} instead of JS literals like new Date()
- Save files as UTF-8 without BOM; lint top-level structure as an array
When it happens
Trigger: A .json migration containing invalid Extended JSON (e.g. unquoted keys, ISO dates not in {"$date":...} form), a top-level object instead of an array of commands, or BOM/encoding issues in the file.
Common situations: Hand-written migration JSON with JavaScript-style syntax, migrating from JS driver scripts to golang-migrate JSON migrations, editors writing UTF-8 BOM, typo like {createdDate: new Date()} instead of {"$date": ...}.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
Related errors
- can't read limit argument N
- no database name
- no config
- both x-advisory-lock-timeout-interval and x-advisory-lock-ti
- no config
AI-assisted analysis of golang-migrate/migrate@01a9643f14 (2026-09-02).
Data as JSON: /api/errors/8350b34837da9d6d.
Report an issue: GitHub.