paperclipai/paperclip · error

An import is already running for this account

Error message

An import is already running for this account

What it means

Import concurrency guard (adopt case): a board actor already has a running import job for the identical request signature, so a resubmit gets 409 carrying the running job's id and status URL, letting the client adopt the in-flight job instead of double-importing.

Solutions

  1. Wait for the conflicting state to clear (running job, hold, pause, or stale fingerprint), refresh the current state, then retry the operation.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at server/src/routes/companies.ts:605 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-08-18). Data as JSON: /api/errors/c1481e32d571ba0c. Report an issue: GitHub.

Appendix: source

Thrown at server/src/routes/companies.ts:638

    const operation = async () => {
      try {
        const importBody = companyPortabilityImportSchema.parse(rawImportBody);
        assertImportTargetAccess(req, importBody.target);
        const activity = importedCompanyActivityContext(actor, importBody.include ?? null);
        const result = await portability.importBundle(importBody, boardUserId, {
          pauseAutomations: importBody.pauseAutomations === true,
        });
        // The import is committed. Settlement (hooks) runs before the
        // best-effort audit entry so a logging failure cannot make a
        // committed import read as failed — or, on the transfer path, release
        // the apply claim and invite a duplicate re-import.
        await hooks?.onSuccess?.(result);
        try {
          await logImportedCompanyActivity(db, activity, result);
        } catch (activityError) {
          logger.warn(
            { err: activityError, companyId: result.company.id },
            "failed to write the company.imported activity entry for a committed import",
          );
        }
        return result;
      } catch (error) {
        await hooks?.onFailure?.(errorMessage(error));
        throw error;
      }
    };
    if (wantsAsyncImport(req)) {
      // Async job path. Two kinds of callers opt in:
      //  - trusted Cloud tenants (original behavior, kept byte-identical),
      //    keyed by their tenant identity headers;
      //  - any other board session, keyed by its user id, so long imports
      //    survive proxies cutting the connection while the server finishes.
      // Jobs are held in memory only and are lost on restart — existing
      // semantics; the status route above 404s unknown ids, so a client
      // that can no longer see its job treats it as gone and resubmits.
      cleanupTerminalImportJobs(importJobs, importJobTerminalRetentionMs);

View on GitHub (pinned to 01ad858492)