Budibase/budibase · error · Error

1:N Relationship Error: Record already linked to another.

Error message

1:N Relationship Error: Record already linked to another.

What it means

Budibase enforces the "one" side of a one-to-many relationship being exclusive: when saving a row, for each linked ID in a 1:N field, the controller looks up existing link documents for that target row (excluding links already pointing back to the row being saved). If any other row is already linked to the target, saving would silently steal the target, so the LinkController throws this error instead. It is a data-integrity guard inside rowSaved, reached via updateLinks during row save.

Source

Thrown at packages/server/src/db/linkedRows/LinkController.ts:241

              await getLinkDocuments({
                tableId: field.tableId,
                rowId: linkId,
              })
            ).filter(
              link =>
                link.id !== row._id && link.fieldName === linkedSchema.name
            )

            // check all the related rows exist
            const foundRecords = await this._db.getMultiple(
              links.map(l => l.id),
              { allowMissing: true, excludeDocs: true }
            )

            // The 1 side of 1:N is already related to something else
            // You must remove the existing relationship
            if (foundRecords.length > 0) {
              throw new Error(
                `1:N Relationship Error: Record already linked to another.`
              )
            }
          }

          if (linkId && linkId !== "" && linkDocIds.indexOf(linkId) === -1) {
            // first check the doc we're linking to exists
            try {
              await this._db.get(linkId)
            } catch (err) {
              // skip links that don't exist
              continue
            }
            operations.push(
              new LinkDocument(
                table._id!,
                fieldName,
                row._id!,

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Load the child row's current parent first and remove that link (save the old parent row without the child in its link field) before assigning it to the new parent.
  2. Retry the save in the UI/automation if it was a race, and make the two writers coordinate (e.g. re-fetch the row and apply the assignment once).
  3. If the relationship should allow many-to-many, change the relationship type on the table so exclusivity is not enforced.
  4. For bulk imports, de-duplicate child assignments so each child appears under only one parent per batch.

Example fix

// before
destRow.child = [childRow._id]
await saveRow(destRow) // throws: Record already linked to another
// after
const oldParent = await findParentOf(childRow._id)
if (oldParent) {
  oldParent.child = oldParent.child.filter(id => id !== childRow._id)
  await saveRow(oldParent)
}
destRow.child = [childRow._id]
await saveRow(destRow)
Defensive patterns

Strategy: validation

Validate before calling

const links = await getLinkDocuments({ tableId: childTableId, rowId: childRowId })
const otherParent = links.find(l => l.id !== destRowId && l.fieldName === linkFieldName)
if (otherParent) throw new Error("Child already linked to another parent")

Try / catch

try {
  await saveRow(row)
} catch (err) {
  if (err.message.includes("Record already linked to another")) {
    // reload row, clear old parent's link, then retry assignment
  } else throw err
}

Prevention

When it happens

Trigger: Saving/updating a row where a one-to-many link field is set to a record that is already linked from a different parent row; the request runs LinkController.rowSaved via updateLinks and the getLinkDocuments lookup for the target linkId returns at least one existing link owned by another row.

Common situations: Two users concurrently assign the same child record to different parents; an automation/bulk import assigns a child row that is already linked elsewhere; switching a child's parent without first clearing the old parent's link; restoring or copying rows that carry stale link values.

Related errors


AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29). Data as JSON: /api/errors/5fa8ed311da4715a. Report an issue: GitHub.