Netflix/chaosmonkey · warning

schedule already exists

Error message

schedule already exists

What it means

ErrAlreadyExists is the sentinel error returned by SchedStore.Publish (and PublishWithDelay) when a chaos-monkey termination schedule for the given date already exists in the store. The MySQL implementation detects a duplicate during the insert transaction and returns this sentinel even if the transaction commit itself fails, per the comment at mysql/mysql.go:164. It lets callers distinguish 'already published' from other storage failures.

Source

Thrown at schedstore/schedstore.go:26

//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package schedstore

import (
	"errors"
	"time"

	"github.com/Netflix/chaosmonkey/v2/schedule"
)

// ErrAlreadyExists is returned when calling Publish if a schedule already
// exists
var ErrAlreadyExists = errors.New("schedule already exists")

// SchedStore stores schedule of terminations
type SchedStore interface {
	// Retrieve retrieves the schedule for the given date
	// The date must be in the local time zone
	Retrieve(date time.Time) (*schedule.Schedule, error)

	// Publish publishes the schedule for the given date
	// The date must be in the local time zone
	Publish(date time.Time, sched *schedule.Schedule) error
}

View on GitHub (pinned to eaa28fb761)

Solutions

  1. Check for existence first or treat ErrAlreadyExists as an expected no-op: use errors.Is(err, schedstore.ErrAlreadyExists) and skip/continue
  2. Run only one scheduler instance at a time (lock or leader election) to avoid duplicate publishes
  3. If a stale duplicate must be replaced, delete the existing schedule row for that date before republishing

Example fix

// before
if err := store.Publish(sched); err != nil {
    log.Fatal(err)
}
// after
if err := store.Publish(sched); err != nil {
    if errors.Is(err, schedstore.ErrAlreadyExists) {
        log.Println("schedule already published for today; skipping")
        return nil
    }
    log.Fatal(err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

var exists bool
err := db.QueryRow("SELECT EXISTS(SELECT 1 FROM sched WHERE schedDate=?)", date.Format("2006-01-02")).Scan(&exists)
if err == nil && exists { return nil // skip publish }

Type guard

func IsAlreadyExists(err error) bool { return errors.Is(err, schedstore.ErrAlreadyExists) }

Try / catch

if err := store.Publish(sched); err != nil {
    if errors.Is(err, schedstore.ErrAlreadyExists) {
        // expected: schedule for this date already published
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: Calling schedstore.Publish or PublishWithDelay for a date for which a schedule row already exists in the MySQL sched table; concurrent Publish calls racing to insert the same date's schedule.

Common situations: A scheduler cron job running twice on the same day; re-running an outbox/dispatch process after a partial failure; two instances of the chaos monkey coordinator both attempting to publish the daily schedule.

Related errors


AI-assisted analysis of Netflix/chaosmonkey@eaa28fb761 (2026-09-03). Data as JSON: /api/errors/d7b2a07d84644884. Report an issue: GitHub.