{"record":{"id":"7ceba8342d33a7ee","repo":"navidrome/navidrome","slug":"schedule-id-q-already-exists","errorCode":null,"errorMessage":"schedule ID %q already exists","messagePattern":"schedule ID %q already exists","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/host_scheduler.go","lineNumber":72,"sourceCode":"func newSchedulerService(pluginName string, manager *Manager, sched scheduler.Scheduler) *schedulerServiceImpl {\n\treturn &schedulerServiceImpl{\n\t\tpluginName: pluginName,\n\t\tmanager:    manager,\n\t\tscheduler:  sched,\n\t\tschedules:  make(map[string]*scheduleEntry),\n\t}\n}\n\nfunc (s *schedulerServiceImpl) ScheduleOneTime(ctx context.Context, delaySeconds int32, payload string, scheduleID string) (string, error) {\n\tif scheduleID == \"\" {\n\t\tscheduleID = id.NewRandom()\n\t}\n\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\n\tif _, exists := s.schedules[scheduleID]; exists {\n\t\treturn \"\", fmt.Errorf(\"schedule ID %q already exists\", scheduleID)\n\t}\n\n\tcapturedID := scheduleID\n\ttimer := timeAfterFunc(time.Duration(delaySeconds)*time.Second, func() {\n\t\ts.invokeCallback(context.Background(), capturedID)\n\t\t// Clean up the entry after firing\n\t\ts.mu.Lock()\n\t\tdelete(s.schedules, capturedID)\n\t\ts.mu.Unlock()\n\t})\n\n\ts.schedules[scheduleID] = &scheduleEntry{\n\t\tpluginName:  s.pluginName,\n\t\tpayload:     payload,\n\t\tisRecurring: false,\n\t\ttimer:       timer,\n\t}\n","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/navidrome/navidrome/blob/4ed7494a3293a9e9e647897ebfb9be327efd981b/plugins/host_scheduler.go#L54-L90","documentation":"HostScheduler.ScheduleOneTime registers a one-shot timer under a caller-supplied scheduleID. Before registering it locks the map and rejects any ID that is already present in s.schedules, returning this error instead of overwriting. Duplicate IDs are never silently replaced — the caller must pick a unique ID.","triggerScenarios":"Calling ScheduleOneTime(ctx, scheduleID, ...) with a scheduleID that was previously registered (by ScheduleOneTime or ScheduleRecurring) and has not yet fired or been cancelled.","commonSituations":"Re-registering the same static ID on plugin restart without clearing prior state; retry logic calling ScheduleOneTime with the same ID after a transient failure elsewhere; two components in one plugin hardcoding the same schedule name; a one-time schedule that has not fired yet being scheduled again.","solutions":["Generate unique IDs (e.g. fmt.Sprintf(\"job-%d\", time.Now().UnixNano()) or a UUID) instead of hardcoded names.","Check-and-skip: treat the error as 'already scheduled' if re-registration is expected and harmless.","Cancel the existing schedule (CancelSchedule with the old ID) before re-registering the same ID.","Namespace IDs per component/plugin feature to avoid collisions (\"report-daily\" vs \"cleanup-daily\").","On restart, rebuild schedule state or use CancelAll/Shutdown before re-registering."],"exampleFix":"// before\nscheduler.ScheduleOneTime(ctx, \"report\", payload, 60)\n// after\nid := fmt.Sprintf(\"report-%d\", time.Now().UnixNano())\nscheduler.ScheduleOneTime(ctx, id, payload, 60)","handlingStrategy":"try-catch","validationCode":"// best-effort: cancel any prior registration with the same ID first\n_ = scheduler.CancelSchedule(ctx, scheduleID) // no-op if absent\nif id, err := scheduler.ScheduleOneTime(ctx, scheduleID, payload, delay); err != nil {\n    return err\n} else {\n    _ = id\n}","typeGuard":null,"tryCatchPattern":"id, err := scheduler.ScheduleOneTime(ctx, scheduleID, payload, delaySeconds)\nif err != nil {\n    if strings.Contains(err.Error(), \"already exists\") {\n        return nil // idempotent: already scheduled\n    }\n    return err\n}","preventionTips":["Derive schedule IDs from UUIDs or timestamps, not hardcoded strings.","Namespace IDs per component (feature + purpose).","Cancel old schedules on shutdown/reload before re-registering.","Treat 'already exists' as idempotent success where re-registration is expected."],"tags":["scheduling","duplicate-key","plugins","host-api"],"backgroundTag":"duplicate-schedule-id","analyzedSha":"4ed7494a3293a9e9e647897ebfb9be327efd981b","analyzedAt":"2026-09-01T05:03:05.018Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}