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

  1. Fix the JSON syntax reported by the wrapped unmarshal error
  2. Ensure the file is an array of command documents: [{"insert": ...}, ...]
  3. Use MongoDB Extended JSON (relaxed or canonical) for dates/ObjectIds: {"$date": "..."}, {"$oid": "..."}
  4. 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

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


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