diff --git a/src/user/user_objects.cc b/src/user/user_objects.cc
index 182d367e..35bd3645 100644
--- a/src/user/user_objects.cc
+++ b/src/user/user_objects.cc
@@ -899,6 +899,11 @@ mjCBody& mjCBody::operator+=(const mjCFrame& other) {
// attach referencing elements
*model += *other.model;
+ // leave the source model in a clean state
+ if (other.model != model) {
+ other.model->key_pending_.clear();
+ }
+
// clear namespace and return body
other.model->prefix.clear();
other.model->suffix.clear();
@@ -1785,6 +1790,11 @@ mjCFrame& mjCFrame::operator+=(const mjCBody& other) {
// attach referencing elements
*model += *other.model;
+ // leave the source model in a clean state
+ if (other.model != model) {
+ other.model->key_pending_.clear();
+ }
+
// clear suffixes and return
other.model->suffix.clear();
other.model->prefix.clear();
diff --git a/test/user/user_api_test.cc b/test/user/user_api_test.cc
index 6977e27a..1f2b309a 100644
--- a/test/user/user_api_test.cc
+++ b/test/user/user_api_test.cc
@@ -1386,5 +1386,56 @@ TEST_F(MujocoTest, AttachNestedKeyframe) {
mj_deleteModel(m_child);
}
+TEST_F(MujocoTest, RepeatedAttachKeyframe) {
+ static constexpr char xml_1[] = R"(
+
+
+
+
+ )";
+
+ static constexpr char xml_2[] = R"(
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )";
+
+ std::array er;
+ mjSpec* spec_1 = mj_parseXMLString(xml_1, 0, er.data(), er.size());
+ EXPECT_THAT(spec_1, NotNull()) << er.data();
+ mjSpec* spec_2 = mj_parseXMLString(xml_2, 0, er.data(), er.size());
+ EXPECT_THAT(spec_2, NotNull()) << er.data();
+
+ mjsBody* body_1 = mjs_findBody(spec_1, "body");
+ mjsFrame* attachment_frame = mjs_addFrame(body_1, 0);
+ mjs_attachBody(attachment_frame, mjs_findBody(spec_2, "b1"), "b1-", "");
+ mjModel* model_1 = mj_compile(spec_1, 0);
+ EXPECT_THAT(model_1, NotNull());
+ mjs_attachBody(attachment_frame, mjs_findBody(spec_2, "b2"), "b2-", "");
+ mjModel* model_2 = mj_compile(spec_1, 0);
+ EXPECT_THAT(model_2, NotNull());
+
+ EXPECT_EQ(model_1->nkey, 1);
+ EXPECT_EQ(model_2->nkey, 2);
+ EXPECT_STREQ(mj_id2name(model_2, mjOBJ_KEY, 0), "b1-home");
+ EXPECT_STREQ(mj_id2name(model_2, mjOBJ_KEY, 1), "b2-home");
+
+ mj_deleteSpec(spec_1);
+ mj_deleteSpec(spec_2);
+ mj_deleteModel(model_1);
+ mj_deleteModel(model_2);
+}
+
} // namespace
} // namespace mujoco