diff --git a/changelog.d/393.misc b/changelog.d/393.misc
new file mode 100644
index 00000000..54104bb4
--- /dev/null
+++ b/changelog.d/393.misc
@@ -0,0 +1 @@
+Fix PostgreSQL errors when a metric activity is recorded twice
\ No newline at end of file
diff --git a/src/datastore/postgres/PgDatastore.ts b/src/datastore/postgres/PgDatastore.ts
index aa3236db..309d8cc9 100644
--- a/src/datastore/postgres/PgDatastore.ts
+++ b/src/datastore/postgres/PgDatastore.ts
@@ -296,7 +296,10 @@ export class PgDatastore implements Datastore {
         date = date || new Date();
         const userId = (user instanceof SlackGhost) ? user.toEntry().id : user.userId;
 
-        await this.postgresDb.none("INSERT INTO metrics_activities (user_id, room_id, date) VALUES(${userId}, ${roomId}, ${date})", {
+        await this.postgresDb.none(
+            "INSERT INTO metrics_activities (user_id, room_id, date) " +
+            "VALUES(${userId}, ${roomId}, ${date}) " +
+            "ON CONFLICT ON CONSTRAINT cons_activities_unique DO NOTHING;", {
             date: `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`,
             roomId: room.toEntry().id,
             userId,
diff --git a/src/tests/integration/SharedDatastoreTests.ts b/src/tests/integration/SharedDatastoreTests.ts
index 628c6e41..87764374 100644
--- a/src/tests/integration/SharedDatastoreTests.ts
+++ b/src/tests/integration/SharedDatastoreTests.ts
@@ -277,4 +277,28 @@ export const doDatastoreTests = (ds: () => Datastore, roomsAfterEach: () => void
             });
         });
     });
+
+    describe("metrics", () => {
+        it("should not throw when an activity is upserted twice", async () => {
+            const user = SlackGhost.fromEntry(null as any, {
+                display_name: "A displayname",
+                avatar_url: "Some avatar",
+                id: "someid1",
+                slack_id: "FOOBAR",
+                team_id: "BARBAZ",
+            }, null);
+            const room = new BridgedRoom({} as any, {
+                inbound_id: "a_remote_id",
+                matrix_room_id: "a_matrix_id",
+                slack_channel_id: "a_channel_id",
+                slack_channel_name: "a_channel_name",
+                slack_team_id: "a_team_id",
+                slack_webhook_uri: "a_webhook_uri",
+                puppet_owner: undefined,
+            }, {} as any);
+            const date = new Date();
+            await ds().upsertActivityMetrics(user, room, date);
+            await ds().upsertActivityMetrics(user, room, date);
+        });
+    });
 };