CherryHQ/cherry-studio · error · Error

Heartbeat workspace must be user-owned: ${workspace.workspac

Error message

Heartbeat workspace must be user-owned: ${workspace.workspaceId}

What it means

Thrown during heartbeat handling in runAgentTask: the heartbeat resolves its target workspace row (via the DataApi), and that workspace's type is not AGENT_WORKSPACE_TYPE.USER. Heartbeats read heartbeat.md from a real on-disk user workspace path, so a system/builtin workspace is not a valid heartbeat target.

Source

Thrown at src/main/ai/agents/runAgentTask.ts:195

        throw new Error(`Unsupported heartbeat workspace source: ${String(exhaustive)}`)
      }
    }
    let workspaceRow: Awaited<ReturnType<typeof agentWorkspaceService.getById>>
    try {
      workspaceRow = agentWorkspaceService.getById(workspace.workspaceId)
    } catch (error) {
      if (isDataApiError(error) && error.code === ErrorCode.NOT_FOUND) {
        logger.debug('Heartbeat skipped (workspace deleted)', {
          agentId,
          scheduleId,
          workspaceId: workspace.workspaceId
        })
        return { sessionId: null, result: 'Skipped (workspace deleted)' }
      }
      throw error
    }
    if (workspaceRow.type !== AGENT_WORKSPACE_TYPE.USER) {
      throw new Error(`Heartbeat workspace must be user-owned: ${workspace.workspaceId}`)
    }
    const workspacePath = workspaceRow.path
    const content = await readHeartbeat(workspacePath)
    if (!content) {
      logger.debug('Heartbeat skipped (no heartbeat.md)', { agentId, scheduleId })
      return { sessionId: null, result: 'Skipped (no file)' }
    }
    effectivePrompt = [
      '[Heartbeat]',
      'This is a periodic heartbeat. The instructions below are from your heartbeat.md file.',
      'Process each item, take action where possible, and use the notify tool to alert the user of important results.',
      '',
      '---',
      content
    ].join('\n')
  }

  const expectedReuseRevision = normalizeTaskSessionReuseRevision(ctx.input.reuseRevision)

View on GitHub (pinned to 726446b54c)

Solutions

  1. Re-point the agent's heartbeat schedule at a user-owned workspace (AGENT_WORKSPACE_TYPE.USER).
  2. When changing a workspace type, validate/migrate any heartbeat schedules bound to it.
  3. If the workspace was deleted/recreated, update the schedule's workspaceId to the new user workspace.
  4. Disable the heartbeat (config.heartbeat_enabled = false) for agents that legitimately use a system workspace.

Example fix

// before: heartbeat scheduled against a system workspace
schedule: { workspace: { type: 'system' } }

// after: bind heartbeat to a user workspace
schedule: { workspace: { type: 'user', workspaceId: 'ws-123' } }
Defensive patterns

Strategy: validation

Validate before calling

import { AGENT_WORKSPACE_TYPE } from '@shared/data/api/schemas/agentWorkspaces'
const ws = await resolveWorkspace(workspace)
if (!ws || ws.type !== AGENT_WORKSPACE_TYPE.USER) {
  // disable the heartbeat or rebind the schedule to a user workspace
  await jobScheduleService.update(scheduleId, { active: false })
  return
}

Type guard

function isUserWorkspace(w: { type: string } | null | undefined): boolean {
  return w?.type === 'user'
}

Try / catch

try {
  return await runAgentTask(ctx)
} catch (e) {
  if (e instanceof Error && /Heartbeat workspace must be user-owned/.test(e.message)) {
    // rebind or disable the misconfigured heartbeat schedule
    await jobScheduleService.update(scheduleId, { active: false }).catch(() => {})
    return { sessionId: null, result: 'Skipped (heartbeat misconfigured)' }
  } else throw e
}

Prevention

When it happens

Trigger: A heartbeat-enabled agent task runs (taskName === HEARTBEAT_TASK_NAME and prompt === HEARTBEAT_PROMPT_SENTINEL) whose resolved workspace row has type 'system' (or any non-user type).

Common situations: A heartbeat schedule was configured against a system workspace instead of a user workspace; a workspace was downgraded/changed to system after the schedule was created; stale schedule rows referencing the wrong workspace type.

Related errors


AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12). Data as JSON: /api/errors/64c03a64e720bf6b. Report an issue: GitHub.