hcengineering/platform · error

Document ${documentName} already exists

Error message

Document ${documentName} already exists

What it means

The createContent RPC checks hocuspocus.documents and hocuspocus.loadingDocuments before creating a new document. If a document with the same name is already loaded (or currently being loaded), it throws this error to prevent duplicate in-memory documents. The RPC is intended for creating documents that don't yet exist server-side.

Source

Thrown at server/collaborator/src/rpc/methods/createContent.ts:37

  decodeDocumentId
} from '@hcengineering/collaborator-client'
import { saveCollabJson } from '@hcengineering/collaboration'
import { type Blob, type Ref, MeasureContext } from '@hcengineering/core'
import { Context } from '../../context'
import { RpcMethodParams } from '../rpc'

export async function createContent (
  ctx: MeasureContext,
  context: Context,
  documentName: string,
  payload: CreateContentRequest,
  params: RpcMethodParams
): Promise<CreateContentResponse> {
  const { content } = payload
  const { hocuspocus, storageAdapter } = params

  if (hocuspocus.documents.has(documentName) || hocuspocus.loadingDocuments.has(documentName)) {
    throw new Error(`Document ${documentName} already exists`)
  }

  const { documentId } = decodeDocumentId(documentName)

  const result: Record<string, Ref<Blob>> = {}
  for (const [field, markup] of Object.entries(content)) {
    const blob = await saveCollabJson(ctx, storageAdapter, context.wsIds, documentId, markup)
    result[field] = blob
  }

  return { content: result }
}

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Treat this as a success/no-op if the existing document is yours: catch the error and proceed to connect instead of creating.
  2. Check client-side whether the document already exists (or is open) before calling createContent.
  3. Debounce/guard create RPCs so concurrent/duplicate requests aren't sent for the same documentName.
  4. Use a unique documentName (new document id) if a genuinely new document is intended.

Example fix

// before
await rpc.createContent({ documentName, content })
connectProvider(documentName)

// after
try {
  await rpc.createContent({ documentName, content })
} catch (e) {
  if (!String(e.message).includes('already exists')) throw e
}
connectProvider(documentName)
Defensive patterns

Strategy: try-catch

Validate before calling

// server-side pre-check before RPC
if (hocuspocus.documents.has(documentName) || hocuspocus.loadingDocuments.has(documentName)) {
  return existingDocumentState // skip create
}
// client-side pre-check: don't create for a doc already open
if (openProviders.has(documentName)) return

Try / catch

try {
  await rpc.createContent({ documentName, content })
} catch (err) {
  if (String(err.message).includes('already exists')) {
    // idempotent: document is already available, just connect
    return
  }
  throw err
}

Prevention

When it happens

Trigger: Calling the createContent RPC with a documentName that is already open in the Hocuspocus server (an active collaboration session) or is mid-load; concurrent createContent calls for the same document; or a client creating content for a document it already has a provider connected to.

Common situations: Double-click/double-submit on a 'create' button, retry logic re-issuing createContent after a slow first response, frontend opening the doc before the create RPC completes, or reusing a document name across create calls.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/6ca91583c67de2e5. Report an issue: GitHub.