nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: go-co-op/gocron
Pull request #74 (2020-11-10 04:14:07)

add mutex to job
### What does this do?
adds a mutex to the job struct to avoid race conditions

### Which issue(s) does this PR fix/relate to?
<!--- Put `Resolves #XXX` here to auto-close the issue that your PR fixes (if such) --->
#73 

### List any changes that modify/break current functionality


### Have you included tests for your changes?


### Did you document any new/modified functionality?

- [ ] Updated `example_test.go`
- [ ] Updated `README.md`

### Notes



Patch:
diff --git a/job.go b/job.go
index 65f5bdd..9a9bca5 100644
--- a/job.go
+++ b/job.go
@@ -2,11 +2,13 @@ package gocron
 
 import (
 	"fmt"
+	"sync"
 	"time"
 )
 
 // Job struct stores the information necessary to run a Job
 type Job struct {
+	sync.RWMutex
 	interval          uint64                   // pause interval * unit between runs
 	unit              timeUnit                 // time units, ,e.g. 'minutes', 'hours'...
 	startsImmediately bool                     // if the Job should run upon scheduler start
@@ -44,11 +46,13 @@ func NewJob(interval uint64) *Job {
 
 // Run the Job and immediately reschedule it
 func (j *Job) run() {
+	j.Lock()
+	defer j.Unlock()
 	callJobFuncWithParams(j.funcs[j.jobFunc], j.fparams[j.jobFunc])
 	j.runCount++
 }
 
-func (j Job) neverRan() bool {
+func (j *Job) neverRan() bool {
 	return j.lastRun.IsZero()
 }
 
diff --git a/scheduler.go b/scheduler.go
index f1e80b2..eeefabc 100644
--- a/scheduler.go
+++ b/scheduler.go
@@ -88,6 +88,8 @@ func (s *Scheduler) ChangeLocation(newLocation *time.Location) {
 
 // scheduleNextRun Compute the instant when this Job should run next
 func (s *Scheduler) scheduleNextRun(j *Job) {
+	j.Lock()
+	defer j.Unlock()
 	now := s.time.Now(s.loc)
 
 	if j.startsImmediately {


Test patch:
diff --git a/scheduler_test.go b/scheduler_test.go
index c67fd70..b961597 100644
--- a/scheduler_test.go
+++ b/scheduler_test.go
@@ -252,17 +252,17 @@ func TestSwap(t *testing.T) {
 	s.Every(2).Minute().Do(task)
 
 	jb := s.Jobs()
-	var jobsBefore []Job
+	var jobsBefore []*Job
 	for _, p := range jb {
-		jobsBefore = append(jobsBefore, *p)
+		jobsBefore = append(jobsBefore, p)
 	}
 
 	s.Swap(1, 0)
 
 	jobsAfter := s.Jobs()
 
-	assert.Equal(t, &jobsBefore[0], jobsAfter[1])
-	assert.Equal(t, &jobsBefore[1], jobsAfter[0])
+	assert.Equal(t, jobsBefore[0], jobsAfter[1])
+	assert.Equal(t, jobsBefore[1], jobsAfter[0])
 }
 
 func TestLess(t *testing.T) {
Repository: go-co-op/gocron
Pull request #144 (2021-03-24 13:30:42)

#142 validation of params is added
### What does this do?

It introduces additional validation of params in Scheduler.Do. If list of passed params doesn't correspond to original function params it returns an error.

### Which issue(s) does this PR fix/relate to?
#142 


### List any changes that modify/break current functionality


### Have you included tests for your changes?

TestScheduler_LessParamsThanExpected
TestScheduler_MoreParamsThanExpected

### Did you document any new/modified functionality?

- [ ] Updated `example_test.go`
- [ ] Updated `README.md`

### Notes


JohnRoesler: @mikhailbolshakov thanks for the PR! this is great stuff. I'm going to drop a couple of comments just on the testing format.

Patch:
diff --git a/gocron.go b/gocron.go
index d811340..797a527 100644
--- a/gocron.go
+++ b/gocron.go
@@ -28,6 +28,7 @@ var (
 	ErrAtTimeNotSupported  = errors.New("the At() method is not supported for this time unit")
 	ErrWeekdayNotSupported = errors.New("weekday is not supported for time unit")
 	ErrTagsUnique          = func(tag string) error { return fmt.Errorf("a non-unique tag was set on the job: %s", tag) }
+	ErrWrongParams         = errors.New("wrong list of params")
 )
 
 func wrapOrError(toWrap error, err error) error {
diff --git a/scheduler.go b/scheduler.go
index b8b6aba..c3e1d4a 100644
--- a/scheduler.go
+++ b/scheduler.go
@@ -529,6 +529,13 @@ func (s *Scheduler) Do(jobFun interface{}, params ...interface{}) (*Job, error)
 		return nil, ErrNotAFunction
 	}
 
+	f := reflect.ValueOf(jobFun)
+	if len(params) != f.Type().NumIn() {
+		s.RemoveByReference(job)
+		job.error = wrapOrError(job.error, ErrWrongParams)
+		return nil, job.error
+	}
+
 	fname := getFunctionName(jobFun)
 	job.functions[fname] = jobFun
 	job.params[fname] = params


Test patch:
diff --git a/scheduler_test.go b/scheduler_test.go
index 3288ce9..72746e7 100644
--- a/scheduler_test.go
+++ b/scheduler_test.go
@@ -1058,3 +1058,25 @@ func TestScheduler_TagsUnique(t *testing.T) {
 	assert.EqualError(t, err, ErrTagsUnique(bar).Error())
 
 }
+
+func TestScheduler_DoParameterValidation(t *testing.T) {
+	testCases := []struct {
+		description string
+		parameters  []interface{}
+	}{
+		{"less than expected", []interface{}{"p1"}},
+		{"more than expected", []interface{}{"p1", "p2", "p3"}},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.description, func(t *testing.T) {
+			s := NewScheduler(time.UTC)
+			f := func(s1, s2 string) {
+				fmt.Println("ok")
+			}
+
+			_, err := s.Every(1).Days().StartAt(time.Now().UTC().Add(time.Second*10)).Do(f, tc.parameters...)
+			assert.EqualError(t, err, ErrWrongParams.Error())
+		})
+	}
+}
Repository: go-co-op/gocron
Pull request #194 (2021-06-10 14:43:10)

handle creating a job after an update and delete
### What does this do?
after a job was updated, we didn't clear the update flag on the scheduler which caused a nil pointer exception on the creation of a new job

### Which issue(s) does this PR fix/relate to?
<!--- Put `Resolves #XXX` here to auto-close the issue that your PR fixes (if such) --->
resolves https://github.com/go-co-op/gocron/issues/193

### List any changes that modify/break current functionality


### Have you included tests for your changes?
👍 

### Did you document any new/modified functionality?

- [ ] Updated `example_test.go`
- [ ] Updated `README.md`

### Notes



Patch:
diff --git a/scheduler.go b/scheduler.go
index 9d918a2..edd90d8 100644
--- a/scheduler.go
+++ b/scheduler.go
@@ -918,6 +918,7 @@ func (s *Scheduler) Update() (*Job, error) {
 	if !s.updateJob {
 		return job, wrapOrError(job.error, ErrUpdateCalledWithoutJob)
 	}
+	s.updateJob = false
 	job.stop()
 	job.ctx, job.cancel = context.WithCancel(context.Background())
 	return s.Do(job.function, job.parameters...)


Test patch:
diff --git a/scheduler_test.go b/scheduler_test.go
index d00a766..0cc3ba1 100644
--- a/scheduler_test.go
+++ b/scheduler_test.go
@@ -1237,6 +1237,39 @@ func TestScheduler_Update(t *testing.T) {
 		_, err = s.Update()
 		assert.EqualError(t, err, ErrUpdateCalledWithoutJob.Error())
 	})
+
+	t.Run("update, delete, create", func(t *testing.T) {
+		s := NewScheduler(time.UTC)
+
+		var counterMutex sync.RWMutex
+		counter := 0
+
+		j, err := s.Every(1).Day().Do(func() { counterMutex.Lock(); defer counterMutex.Unlock(); counter++ })
+		require.NoError(t, err)
+
+		s.StartAsync()
+
+		time.Sleep(300 * time.Millisecond)
+		_, err = s.Job(j).Every("500ms").Update()
+		require.NoError(t, err)
+
+		time.Sleep(550 * time.Millisecond)
+		s.RemoveByReference(j)
+
+		j, err = s.Every("750ms").WaitForSchedule().Do(func() { counterMutex.Lock(); defer counterMutex.Unlock(); counter++ })
+		require.NoError(t, err)
+
+		time.Sleep(800 * time.Millisecond)
+		_, err = s.Job(j).CronWithSeconds("*/1 * * * * *").Update()
+		require.NoError(t, err)
+
+		time.Sleep(time.Second)
+		s.Stop()
+
+		counterMutex.RLock()
+		defer counterMutex.RUnlock()
+		assert.Equal(t, 4, counter)
+	})
 }
 
 func TestScheduler_RunByTag(t *testing.T) {
Repository: go-co-op/gocron
Pull request #253 (2021-11-07 00:46:50)

Append tags rather than overwriting
### What does this do?
This PR changes `scheduler.Tag()` to append to, rather than overwrite, existing tags

### Which issue(s) does this PR fix/relate to?
I didn't create an issue because the README mentioned small changes can just send a PR. Here are some details I would have put in the issue:

#### Describe Bug
If multiple calls to `scheduler.Tag()` are chained together, only the last one called persists.

#### How To Reproduce
Check out [this Playground](https://play.golang.org/p/8ozf_TpCppd)

#### Expected Behavior
I expected multiple calls to `Tag()` to append to existing tags rather than overwrite.

### List any changes that modify/break current functionality
This shouldn't be a breaking change unless someone is using multiple calls to `Tag()` to conditionally overwrite previous ones.

### Have you included tests for your changes?
Yes

### Did you document any new/modified functionality?
No

- [ ] Updated `example_test.go`
- [ ] Updated `README.md`

### Notes
While this was unexpected behavior to me, I understand it might be the intended way to use Tags, in which case this can be closed.



Patch:
diff --git a/scheduler.go b/scheduler.go
index 1df705e..9179e38 100644
--- a/scheduler.go
+++ b/scheduler.go
@@ -788,7 +788,7 @@ func (s *Scheduler) Tag(t ...string) *Scheduler {
 		}
 	}
 
-	job.tags = t
+	job.tags = append(job.tags, t...)
 	return s
 }
 


Test patch:
diff --git a/scheduler_test.go b/scheduler_test.go
index de7feda..8b48683 100644
--- a/scheduler_test.go
+++ b/scheduler_test.go
@@ -1220,6 +1220,20 @@ func TestScheduler_TagsUnique(t *testing.T) {
 
 }
 
+func TestScheduler_MultipleTagsChained(t *testing.T) {
+	const (
+		tag1 = "tag1"
+		tag2 = "tag2"
+	)
+
+	s := NewScheduler(time.UTC)
+
+	j, err := s.Every("1s").Tag(tag1).Tag(tag2).Do(func() {})
+	require.NoError(t, err)
+
+	assert.EqualValues(t, []string{tag1, tag2}, j.Tags())
+}
+
 func TestScheduler_DoParameterValidation(t *testing.T) {
 	testCases := []struct {
 		description string
Repository: go-co-op/gocron
Pull request #271 (2021-12-14 04:58:22)

handle error case better where every/cron is not called
### What does this do?
- in the wrapOrError function checks if the error already exists in the wrapping to avoid dupes
- within the Do() func, check that the interval isn't 0 and error if it is

### Which issue(s) does this PR fix/relate to?
<!--- Put `Resolves #XXX` here to auto-close the issue that your PR fixes (if such) --->


### List any changes that modify/break current functionality


### Have you included tests for your changes?


### Did you document any new/modified functionality?

- [ ] Updated `example_test.go`
- [ ] Updated `README.md`

### Notes



Patch:
diff --git a/gocron.go b/gocron.go
index 09d6462..cb840fe 100644
--- a/gocron.go
+++ b/gocron.go
@@ -37,7 +37,7 @@ var (
 
 func wrapOrError(toWrap error, err error) error {
 	var returnErr error
-	if toWrap != nil {
+	if toWrap != nil && !errors.Is(err, toWrap) {
 		returnErr = fmt.Errorf("%s: %w", err, toWrap)
 	} else {
 		returnErr = err
diff --git a/scheduler.go b/scheduler.go
index 9f42f92..aa0f372 100644
--- a/scheduler.go
+++ b/scheduler.go
@@ -739,6 +739,12 @@ func (s *Scheduler) Do(jobFun interface{}, params ...interface{}) (*Job, error)
 		job.error = wrapOrError(job.error, ErrWeekdayNotSupported)
 	}
 
+	if job.unit != crontab && job.interval == 0 {
+		if job.unit != duration {
+			job.error = wrapOrError(job.error, ErrInvalidInterval)
+		}
+	}
+
 	if job.error != nil {
 		// delete the job from the scheduler as this job
 		// cannot be executed


Test patch:
diff --git a/scheduler_test.go b/scheduler_test.go
index 2232a01..3752af9 100644
--- a/scheduler_test.go
+++ b/scheduler_test.go
@@ -888,6 +888,14 @@ func TestScheduler_Do(t *testing.T) {
 				assert.Zero(t, s.Len(), "The job should be deleted if the arg passed to Do() is not a function")
 			},
 		},
+		{
+			description: "error due to every/cron not called",
+			evalFunc: func(s *Scheduler) {
+				_, err := s.Do(1)
+				assert.EqualError(t, err, ErrInvalidInterval.Error())
+				assert.Zero(t, s.Len(), "The job should be deleted if Every or Cron is not called")
+			},
+		},
 		{
 			description: "positive case",
 			evalFunc: func(s *Scheduler) {
Repository: go-co-op/gocron
Pull request #281 (2022-01-23 18:57:56)

Add .Midday() to schedule a task at 12:00 (#254)
I've added a basic implementation of .Midday() function to allow set a task time to 12:00
Please, feel free to review my PR. Let me know if I missed something.

AlexanderSutul: Hey! Please, feel free to review my PR. Let me know if I missed something.

Patch:
diff --git a/scheduler.go b/scheduler.go
index aa0f372..8bf6e31 100644
--- a/scheduler.go
+++ b/scheduler.go
@@ -986,6 +986,10 @@ func (s *Scheduler) Weekday(weekDay time.Weekday) *Scheduler {
 	return s
 }
 
+func (s *Scheduler) Midday() *Scheduler {
+	return s.At("12:00")
+}
+
 // Monday sets the start day as Monday
 func (s *Scheduler) Monday() *Scheduler {
 	return s.Weekday(time.Monday)


Test patch:
diff --git a/example_test.go b/example_test.go
index 581b0a7..05b9697 100644
--- a/example_test.go
+++ b/example_test.go
@@ -353,6 +353,12 @@ func ExampleScheduler_Location() {
 	// UTC
 }
 
+func ExampleScheduler_Midday() {
+	s := gocron.NewScheduler(time.UTC)
+	_, _ = s.Every(1).Day().Midday().Do(task)
+	s.StartAsync()
+}
+
 func ExampleScheduler_Millisecond() {
 	s := gocron.NewScheduler(time.UTC)
 
diff --git a/scheduler_test.go b/scheduler_test.go
index 3752af9..873e75f 100644
--- a/scheduler_test.go
+++ b/scheduler_test.go
@@ -1682,6 +1682,19 @@ func TestScheduler_CallNextWeekDay(t *testing.T) {
 
 }
 
+func TestScheduler_Midday(t *testing.T) {
+	currentMidday := time.Date(2022, time.January, 0, 12, 0, 0, 0, time.UTC)
+	expectedTime := 24 * time.Hour
+
+	t.Run("check time till next midday", func(t *testing.T) {
+		s := NewScheduler(time.UTC)
+		job, _ := s.Every(1).Day().Midday().Do(func() {})
+		job.lastRun = currentMidday
+		durationToNextTime := s.durationToNextRun(currentMidday, job).duration
+		assert.Equal(t, expectedTime, durationToNextTime)
+	})
+}
+
 func TestScheduler_CheckNextWeekDay(t *testing.T) {
 	januaryFirst2020At := func(hour, minute, second int) time.Time {
 		return time.Date(2020, time.January, 1, hour, minute, second, 0, time.UTC)
Repository: go-co-op/gocron
Pull request #432 (2023-03-08 19:50:35)

job: Pad `ScheduledAtTime(s)` with leading zeroes
I found it a bit odd that times were printed as `8:3` instead of `08:03` when using these methods, so pad them with leading zeroes for clarity.


Patch:
diff --git a/job.go b/job.go
index 5174b5a..085fe8e 100644
--- a/job.go
+++ b/job.go
@@ -336,17 +336,17 @@ func (j *Job) ScheduledTime() time.Time {
 // If multiple times are set, the earliest time will be returned.
 func (j *Job) ScheduledAtTime() string {
 	if len(j.atTimes) == 0 {
-		return "0:0"
+		return "00:00"
 	}
 
-	return fmt.Sprintf("%d:%d", j.getFirstAtTime()/time.Hour, (j.getFirstAtTime()%time.Hour)/time.Minute)
+	return fmt.Sprintf("%02d:%02d", j.getFirstAtTime()/time.Hour, (j.getFirstAtTime()%time.Hour)/time.Minute)
 }
 
 // ScheduledAtTimes returns the specific times of day the Job will run at
 func (j *Job) ScheduledAtTimes() []string {
 	r := make([]string, len(j.atTimes))
 	for i, t := range j.atTimes {
-		r[i] = fmt.Sprintf("%d:%d", t/time.Hour, (t%time.Hour)/time.Minute)
+		r[i] = fmt.Sprintf("%02d:%02d", t/time.Hour, (t%time.Hour)/time.Minute)
 	}
 
 	return r


Test patch:
diff --git a/example_test.go b/example_test.go
index 4bc9b76..b3ba271 100644
--- a/example_test.go
+++ b/example_test.go
@@ -95,7 +95,7 @@ func ExampleJob_ScheduledAtTime() {
 	fmt.Println(job1.ScheduledAtTime())
 	// Output:
 	// 10:30
-	// 8:0
+	// 08:00
 }
 
 func ExampleJob_ScheduledAtTimes() {
@@ -104,7 +104,7 @@ func ExampleJob_ScheduledAtTimes() {
 	s.StartAsync()
 	fmt.Println(job.ScheduledAtTimes())
 	// Output:
-	// [8:0 10:30]
+	// [08:00 10:30]
 }
 
 func ExampleJob_ScheduledTime() {
@@ -657,7 +657,6 @@ func ExampleScheduler_Seconds() {
 	_, _ = s.Every(1).Seconds().Do(task)
 	_, _ = s.Every("1s").Seconds().Do(task)
 	_, _ = s.Every(time.Second).Seconds().Do(task)
-
 }
 
 func ExampleScheduler_SetMaxConcurrentJobs() {
Repository: go-co-op/gocron
Pull request #503 (2023-05-30 17:12:36)

Revert "provide a better panic message when no jobs in scheduler and start blocking called"
Reverts go-co-op/gocron#501

There are cases where it can be called and not cause a deadlock, so I'm just going to leave it alone for now.

example:

```go
package main

import (
	"log"
	"time"

	"github.com/go-co-op/gocron"
)

func main() {
	go func() {
		for {
			time.Sleep(time.Second)
			log.Println("doing something")
		}
	}()

	s := gocron.NewScheduler(time.UTC)
	s.StartBlocking()
}
```


Patch:
diff --git a/scheduler.go b/scheduler.go
index 25feb61..5d2dc0f 100644
--- a/scheduler.go
+++ b/scheduler.go
@@ -77,12 +77,7 @@ func (s *Scheduler) SetMaxConcurrentJobs(n int, mode limitMode) {
 
 // StartBlocking starts all jobs and blocks the current thread.
 // This blocking method can be stopped with Stop() from a separate goroutine.
-//
-// There must be at least 1 job in the scheduler or a panic will occur.
 func (s *Scheduler) StartBlocking() {
-	if len(s.Jobs()) == 0 {
-		panic("gocron: scheduler must have at least 1 job before calling StartBlocking")
-	}
 	s.StartAsync()
 	s.startBlockingStopChanMutex.Lock()
 	s.startBlockingStopChan = make(chan struct{}, 1)


Test patch:
diff --git a/example_test.go b/example_test.go
index f5eb158..229c64d 100644
--- a/example_test.go
+++ b/example_test.go
@@ -776,7 +776,6 @@ func ExampleScheduler_Stop() {
 	fmt.Println(s.IsRunning())
 
 	s = gocron.NewScheduler(time.UTC)
-	_, _ = s.Every(1).Second().Do(task)
 
 	go func() {
 		time.Sleep(1 * time.Second)
diff --git a/scheduler_test.go b/scheduler_test.go
index 00855d7..f758022 100644
--- a/scheduler_test.go
+++ b/scheduler_test.go
@@ -996,8 +996,6 @@ func TestScheduler_Stop(t *testing.T) {
 	})
 	t.Run("stops a running scheduler calling .Stop()", func(t *testing.T) {
 		s := NewScheduler(time.UTC)
-		_, err := s.Every(1).Second().Do(func() {})
-		require.NoError(t, err)
 
 		go func() {
 			time.Sleep(time.Second)
@@ -2707,11 +2705,3 @@ func TestScheduler_WithDistributedLocker_With_Name(t *testing.T) {
 		})
 	}
 }
-
-func TestScheduler_StartBlocking(t *testing.T) {
-	t.Run("panics if no jobs are added", func(t *testing.T) {
-		s := NewScheduler(time.UTC)
-		expected := "gocron: scheduler must have at least 1 job before calling StartBlocking"
-		assert.PanicsWithValue(t, expected, s.StartBlocking)
-	})
-}
Repository: go-co-op/gocron
Pull request #540 (2023-08-16 22:04:12)

Possible data race fix
### What does this do?

* See https://github.com/go-co-op/gocron/issues/539
* Created this to make an example fix


### Which issue(s) does this PR fix/relate to?
<!--- Put `Resolves #XXX` here to auto-close the issue that your PR fixes (if such) --->


### List any changes that modify/break current functionality


### Have you included tests for your changes?


### Did you document any new/modified functionality?

- [ ] Updated `example_test.go`
- [ ] Updated `README.md`

### Notes


JohnRoesler: Nice! Thanks for the fix

Patch:
diff --git a/scheduler.go b/scheduler.go
index 7b763d2..5d1b0cf 100644
--- a/scheduler.go
+++ b/scheduler.go
@@ -892,14 +892,16 @@ func (s *Scheduler) Stop() {
 }
 
 func (s *Scheduler) stop() {
-	s.stopJobs(s.jobs)
+	s.stopJobs()
 	s.executor.stop()
 	s.StopBlockingChan()
 	s.setRunning(false)
 }
 
-func (s *Scheduler) stopJobs(jobs []*Job) {
-	for _, job := range jobs {
+func (s *Scheduler) stopJobs() {
+	s.jobsMutex.RLock()
+	defer s.jobsMutex.RUnlock()
+	for _, job := range s.jobs {
 		job.stop()
 	}
 }


Test patch:
diff --git a/scheduler_test.go b/scheduler_test.go
index 3147222..0ca357d 100644
--- a/scheduler_test.go
+++ b/scheduler_test.go
@@ -2790,3 +2790,24 @@ func TestScheduler_PauseJobExecution(t *testing.T) {
 	assert.GreaterOrEqual(t, counter, 1)
 	assert.LessOrEqual(t, counter, 2)
 }
+
+func TestDataRace(t *testing.T) {
+	// This used to fail with the -race flag
+	sut := NewScheduler(time.UTC)
+	sut.StartAsync()
+	maxRuns := 10
+	task := func() {
+	}
+
+	_, err := sut.
+		Every(1).
+		Milliseconds().
+		Tag("name").
+		SingletonMode().
+		LimitRunsTo(maxRuns).
+		Do(task)
+	assert.NoError(t, err)
+
+	time.Sleep(1 * time.Second)
+	sut.Stop()
+}
Repository: go-co-op/gocron
Pull request #549 (2023-08-23 14:59:06)

don't accept a *Job in Job for Update if the ptr address doesn't match
### What does this do?


### Which issue(s) does this PR fix/relate to?
<!--- Put `Resolves #XXX` here to auto-close the issue that your PR fixes (if such) --->


### List any changes that modify/break current functionality


### Have you included tests for your changes?


### Did you document any new/modified functionality?

- [ ] Updated `example_test.go`
- [ ] Updated `README.md`

### Notes



Patch:
diff --git a/scheduler.go b/scheduler.go
index 9da610e..ce86b48 100644
--- a/scheduler.go
+++ b/scheduler.go
@@ -1327,6 +1327,11 @@ func (s *Scheduler) TagsUnique() {
 // of making changes to the job with the scheduler chain
 // and finalized by calling Update()
 func (s *Scheduler) Job(j *Job) *Scheduler {
+	if job, ok := s.JobsMap()[j.id]; !ok {
+		return s
+	} else if job != j {
+		return s
+	}
 	s.inScheduleChain = &j.id
 	s.updateJob = true
 	return s


Test patch:
diff --git a/scheduler_test.go b/scheduler_test.go
index d287713..356f977 100644
--- a/scheduler_test.go
+++ b/scheduler_test.go
@@ -1759,21 +1759,35 @@ func TestScheduler_DoParameterValidation(t *testing.T) {
 }
 
 func TestScheduler_Job(t *testing.T) {
-	s := NewScheduler(time.UTC)
+	t.Run("happy", func(t *testing.T) {
+		s := NewScheduler(time.UTC)
 
-	j1, err := s.Every("1s").Do(func() {})
-	require.NoError(t, err)
-	assert.Equal(t, j1, s.jobs[j1.id])
+		j1, err := s.Every("1s").Do(func() {})
+		require.NoError(t, err)
+		assert.Equal(t, j1, s.jobs[j1.id])
 
-	j2, err := s.Every("1s").Do(func() {})
-	require.NoError(t, err)
-	assert.Equal(t, j2, s.jobs[j2.id])
+		j2, err := s.Every("1s").Do(func() {})
+		require.NoError(t, err)
+		assert.Equal(t, j2, s.jobs[j2.id])
 
-	s.Job(j1)
-	assert.Equal(t, j1, s.jobs[j1.id])
+		s.Job(j1)
+		assert.Equal(t, j1, s.jobs[j1.id])
 
-	s.Job(j2)
-	assert.Equal(t, j2, s.jobs[j2.id])
+		s.Job(j2)
+		assert.Equal(t, j2, s.jobs[j2.id])
+	})
+	t.Run("job address doesn't match after de/re-referencing", func(t *testing.T) {
+		s := NewScheduler(time.UTC)
+		j1, err := s.Every("1s").Do(func() {})
+		require.NoError(t, err)
+		assert.Equal(t, j1, s.jobs[j1.id])
+
+		j1PtrDeReferenced := *j1
+		j1PtrReReferenced := &j1PtrDeReferenced
+
+		_, err = s.Job(j1PtrReReferenced).Every("2s").Update()
+		assert.EqualError(t, err, ErrUpdateCalledWithoutJob.Error())
+	})
 }
 
 func TestScheduler_Update(t *testing.T) {