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
- Re-point the agent's heartbeat schedule at a user-owned workspace (AGENT_WORKSPACE_TYPE.USER).
- When changing a workspace type, validate/migrate any heartbeat schedules bound to it.
- If the workspace was deleted/recreated, update the schedule's workspaceId to the new user workspace.
- 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
- Bind heartbeat schedules only to user-owned workspaces.
- Validate workspace type when schedules are created or workspaces change type.
- Disable heartbeat for agents on system workspaces.
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
- Cherry Assistant package definition is unavailable
- Cherry Assistant package configuration is invalid: ${invalid
- Agent not found: ${agentId}
- Private key must be a non-empty string
- Provider extension "${providerId}" not registered
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/64c03a64e720bf6b.
Report an issue: GitHub.