toeverything/AFFiNE · error · CopilotFailedToAddWorkspaceArtifact

copilot_failed_to_add_workspace_artifact

copilot_failed_to_add_workspace_artifact

Error message

Failed to add workspace artifact: ${message}

What it means

Catch-all wrapper: any exception from copilotWorkspace.addArtifact that is not already a UserFriendlyError is rethrown as CopilotFailedToAddWorkspaceArtifact (internal_server_error), embedding the original message. The original cause is only visible in the wrapped message and server logs.

Source

Thrown at packages/backend/server/src/plugins/copilot/workspace/resolver.ts:190

    const lockFlag = `${COPILOT_LOCKER}:workspace:${workspaceId}`;
    await using lock = await this.mutex.acquire(lockFlag);
    if (!lock) {
      throw new TooManyRequest('Server is busy');
    }

    const length = Number(ctx.req.headers['content-length']);
    if (length && length >= MAX_EMBEDDABLE_SIZE) {
      throw new BlobQuotaExceeded();
    }

    try {
      return await this.copilotWorkspace.addArtifact(workspaceId, content);
    } catch (e) {
      // passthrough user friendly error
      if (e instanceof UserFriendlyError) {
        throw e;
      }
      throw new CopilotFailedToAddWorkspaceArtifact({
        message: e instanceof Error ? e.message : String(e),
      });
    }
  }

  @Mutation(() => Boolean, {
    name: 'removeWorkspaceArtifact',
    complexity: 2,
    description: 'Remove a workspace artifact',
  })
  async removeArtifact(
    @CurrentUser() user: CurrentUser,
    @Args('workspaceId', { type: () => String })
    workspaceId: string,
    @Args('artifactId', { type: () => String })
    artifactId: string
  ): Promise<boolean> {
    await this.ac

View on GitHub (pinned to b4c8548c09)

Solutions

  1. Read the embedded message and the server logs to identify the underlying exception before the wrap
  2. Verify embedding provider connectivity/keys and database health, then retry the upload
  3. If the same input always fails, inspect the artifact (format/size) - deterministic failures indicate an unsupported input rather than a transient fault
Defensive patterns

Strategy: try-catch

Type guard

const isWorkspaceArtifactFailure = (e: unknown): boolean =>
  Boolean(e && typeof e === 'object' && (e as any).code === 'copilot_failed_to_add_workspace_artifact');

Try / catch

try {
  return await mutate({ workspaceId, blob });
} catch (e) {
  if (e?.code === 'copilot_failed_to_add_workspace_artifact') {
    report(e.message); // wrapped cause: embedding provider / DB / stream error
    return null; // or surface a retry to the user
  }
  throw e;
}

Prevention

When it happens

Trigger: Embedding provider network/API failure mid-processing, database write errors while persisting the artifact, or file stream/read failures inside addArtifact.

Common situations: Embedding API key rotated after the health check passed; transient DB outage; unsupported MIME type hitting an unguarded code path; content-length missing so the size check was skipped and processing choked on an oversized stream.

Related errors


AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18). Data as JSON: /api/errors/e73549f00f594e5e. Report an issue: GitHub.