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
- Check for existence first or treat ErrAlreadyExists as an expected no-op: use errors.Is(err, schedstore.ErrAlreadyExists) and skip/continue
- Run only one scheduler instance at a time (lock or leader election) to avoid duplicate publishes
- 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
- Make publishing idempotent: always handle ErrAlreadyExists explicitly with errors.Is
- Run a single scheduler instance or use a lock/leader election so only one process publishes per date
- Add a UNIQUE constraint on the schedule date so the DB enforces the invariant
- Log ErrAlreadyExists at info level, not as a failure
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
- %s not specified
- unknown group: %v
- database migration failed
- 'attributes' field missing
- 'attributes.chaosMonkey' field missing
AI-assisted analysis of Netflix/chaosmonkey@eaa28fb761 (2026-09-03).
Data as JSON: /api/errors/d7b2a07d84644884.
Report an issue: GitHub.