Repository: dagu-org/dagu

Issue:
[ BUG ] Same dag_run_id Returned for Different DAG Names
While integrating with Dagu, I noticed an unexpected behavior: sometimes different DAGs return the exact same dag_run_id, even though their dag_name values are different.

In this case, I started two different DAGs (dummy-go_fasthttp and dummy-go) and stopped them after 5 seconds, but they returned the same dag_run_id. This leads to incorrect log matching and inconsistent task state updates.

```
[2025-12-10 23:05:28,945: INFO/MainProcess] Task planning.tasks.stop_task[30f3f4b5-71ba-49ad-9c6f-aba10de3019c] received celery-1
[2025-12-10 23:05:28,945: INFO/MainProcess] Task planning.tasks.stop_task[f2dc6e69-3cf2-4b61-bfa8-a4d0baecc86d] received celery-1

[2025-12-10 23:05:28,982: INFO/ForkPoolWorker-2] [TASK 3] Stopping task execution celery-1
[2025-12-10 23:05:28,982: INFO/ForkPoolWorker-3] [TASK 2] Stopping task execution celery-1
[2025-12-10 23:05:28,989: INFO/ForkPoolWorker-1] [TASK 1] Stopping task execution celery-1

[2025-12-10 23:05:28,987: INFO/ForkPoolWorker-2] [TASK 3] Getting dag_info by dag_name=dummy-go_fasthttp celery-1
[2025-12-10 23:05:28,988: INFO/ForkPoolWorker-3] [TASK 2] Getting dag_info by dag_name=dummy-go celery-1
[2025-12-10 23:05:28,993: INFO/ForkPoolWorker-1] [TASK 1] Getting dag_info by dag_name=dummy-main celery-1

[2025-12-10 23:05:29,094: INFO/ForkPoolWorker-1] [TASK 1] Searching for log by dag_run_id=d6f561d4-facc-4e9f-afe3-999aaf5ba2a4 celery-1
[2025-12-10 23:05:29,099: INFO/ForkPoolWorker-2] [TASK 3] Searching for log by dag_run_id=e79b6918-9c57-49a8-a26b-df7de9fe6cd6 celery-1
[2025-12-10 23:05:29,101: INFO/ForkPoolWorker-3] [TASK 2] Searching for log by dag_run_id=e79b6918-9c57-49a8-a26b-df7de9fe6cd6 celery-1

[2025-12-10 23:05:29,111: INFO/ForkPoolWorker-1] [TASK 1] Log found: ID=1462, current stopped_by_id=None celery-1
[2025-12-10 23:05:29,120: INFO/ForkPoolWorker-1] [TASK 1] stopped_by_id=1 set, saved celery-1

[2025-12-10 23:05:29,128: ERROR/ForkPoolWorker-3] Logs do not match by dag_run_id and task: [Started] go_fasthttp (still running) | [Started] go (still running) celery-1

[2025-12-10 23:05:29,169: INFO/ForkPoolWorker-1] [TASK 1] Task successfully stopped celery-1
[2025-12-10 23:05:29,177: INFO/ForkPoolWorker-1] Task planning.tasks.stop_task[f2dc6e69-3cf2-4b61-bfa8-a4d0baecc86d] succeeded in 0.23007729498203844s: None celery-1

[2025-12-10 23:05:29,407: INFO/MainProcess] Task logs.tasks.update_log_from_dagu[1b76ef47-4ae9-4648-88cd-1152d060273e] received celery-1
[2025-12-10 23:05:29,434: INFO/ForkPoolWorker-1] Starting update_log_from_dagu task for log ID: 1462 celery-1

[2025-12-10 23:05:29,642: INFO/MainProcess] Task notifications.tasks.send_to_telegram_channel[5bd80c54-4c71-49f8-a2f2-57a75debd8df] received celery-1
[2025-12-10 23:05:29,643: INFO/ForkPoolWorker-1] Successfully updated log 1462 from Dagu celery-1

[2025-12-10 23:05:29,645: INFO/MainProcess] Task notifications.tasks.send_to_bot_user[73c2a611-ecf9-4005-947a-161e5cf5225e] received celery-1
[2025-12-10 23:05:29,651: INFO/ForkPoolWorker-1] Task logs.tasks.update_log_from_dagu[1b76ef47-4ae9-4648-88cd-1152d060273e] succeeded in 0.24304458900587633s: None celery-1

[2025-12-10 23:05:29,900: INFO/ForkPoolWorker-4] Task notifications.tasks.send_to_bot_user[73c2a611-ecf9-4005-947a-161e5cf5225e] succeeded in 0.2527505260077305s: None celery-1

[2025-12-10 23:05:30,146: ERROR/ForkPoolWorker-3] Logs do not match by dag_run_id and task: [Started] go_fasthttp (still running) | [Started] go (still running) celery-1
[2025-12-10 23:05:31,157: ERROR/ForkPoolWorker-3] Logs do not match by dag_run_id and task: [Started] go_fasthttp (still running) | [Started] go (still running) celery-1

[2025-12-10 23:05:32,158: INFO/ForkPoolWorker-3] [TASK 2] Log found: ID=1460, current stopped_by_id=None celery-1

```

Discussion:
@ghansham: As far as I remember runid is expected to be unique for a given dag but not across dags. Am I missing something @yottahmd ?@yottahmd: @ghansham Yes, that's correct.

Regarding the issue, I suspect there might be a case where the file pattern confuses two different DAGs (with same prefix) when Dagu listing DAG runs.@ghansham: Out of curiosity, @Kaiden0001 in which directory you are looking for log files. They are generally stored in directories which are differentiated by dagnames.@Kaiden0001: The issue isn’t that the dag_run_id values have identical UUIDs, but that when fetching information by dag_name simultaneously, I sometimes receive the wrong record. If I repeat the request a second later, I get the correct one.

For now, I worked around it by adding `{project_id}-{task_id}-...` to the DAG name.

Dagu version: `1.24.11`
`ubuntu server`

Solution patch:
diff --git a/internal/persistence/filedagrun/store.go b/internal/persistence/filedagrun/store.go
index 1695b9dd..2525b0b3 100644
--- a/internal/persistence/filedagrun/store.go
+++ b/internal/persistence/filedagrun/store.go
@@ -500,7 +500,7 @@ func (store *Store) listRoot(_ context.Context, include string) ([]DataRoot, err
 
 	var roots []DataRoot
 	for _, dir := range rootDirs {
-		if include != "" && !strings.Contains(dir, include) {
+		if include != "" && dir != include {
 			continue
 		}
 		if fileutil.IsDir(filepath.Join(store.baseDir, dir)) {



Test patch:
diff --git a/internal/persistence/filedagrun/store_test.go b/internal/persistence/filedagrun/store_test.go
index 8317639b..3cb70db9 100644
--- a/internal/persistence/filedagrun/store_test.go
+++ b/internal/persistence/filedagrun/store_test.go
@@ -314,6 +314,22 @@ func TestListRoot(t *testing.T) {
 	}
 }
 
+// TestListRootExactMatch verifies that listRoot does exact matching, not substring matching.
+// Regression test for issue #1473.
+func TestListRootExactMatch(t *testing.T) {
+	t.Parallel()
+
+	tmpDir := t.TempDir()
+	require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "go"), 0750))
+	require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "go_fasthttp"), 0750))
+
+	store := &Store{baseDir: tmpDir}
+	roots, err := store.listRoot(context.Background(), "go")
+	require.NoError(t, err)
+	require.Len(t, roots, 1, "should only match 'go', not 'go_fasthttp'")
+	assert.Equal(t, "go", roots[0].prefix)
+}
+
 func TestListRootEmptyDirectory(t *testing.T) {
 	t.Parallel()