Context is Everything logo

8. State & Edge Cases

Status Lifecycles

Session Status

idle → active (message sent) → streaming (response in progress) → idle
                                                                 ↘ error → idle

No explicit session close. Sessions become inactive when CLI process exits or times out.

Onboarding Research Status

pending → researching → completed
                      ↘ failed (can retry)

Stored in company_profiles.research_status.

Execution Status

running → completed
        → failed
        → timeout (exceeded timeout_ms)
        → stale (detected as orphaned)
        → cancelled (manual cancellation)

Applies to: execution_history.status, executions.status.

Stale detection: Scheduler checks for executions stuck in 'running' beyond their timeout and marks them 'stale'.

Cloud Mount Health

unmounted → mounting → mounted
                     ↘ degraded → remounting → mounted
                                             ↘ degraded (persistent failure)

Stored in cloud_mounts.health_status.

Cloud Connection Status

pending → authorized (OAuth successful)
        → revoked (user or admin revoked)

Stored in cloud_connections.status.

Schedule State

enabled (cron active) ↔ disabled (cron paused)

schedules.enabled is an integer (0/1). Toggle via /api/scheduler/enable/:name and /disable/:name.

Edge Cases

Authentication

Case Behavior
JWT_SECRET not set Falls back to hardcoded dev default (insecure)
JWT with deleted user Token validates but user lookup fails → 401
Concurrent logins Both sessions valid (no single-session enforcement)
Browser data cleared Session lost, user must re-login. Filesystem sessions survive.
Container restart JWT_SECRET may change (if env-based), invalidating all tokens

Chat Sessions

Case Behavior
Claude CLI crashes mid-response WebSocket error event, partial response shown
WebSocket disconnect Client auto-reconnects, new CLI process spawned
Very large response Streamed token-by-token, no size limit enforced
Concurrent messages from same user Each spawns separate CLI process
API key invalid or expired Error surfaced in chat: "Claude is not configured"
File system full CLI write operations fail, error in response

File Operations

Case Behavior
File exceeds size limit 413 response, file rejected
Path traversal attempt safePath() blocks with error
Duplicate filename upload Overwrites existing file (no versioning)
Delete system file (CLAUDE.md) UI prevents deletion via isCloudItem() / canDeleteFolder() checks
Concurrent file writes SQLite busy_timeout (10s) handles contention, filesystem has no lock

Scheduling

Case Behavior
Duplicate execution (same slot) UNIQUE(schedule_id, scheduled_for) constraint prevents
Schedule fires while previous still running New execution starts (no queue, no backpressure)
Server restart during execution Execution stays 'running', eventually marked 'stale'
Invalid cron expression Validation at creation time, rejected with 400
Timezone edge cases Handled by cron-parser with timezone support
NULL cron_expression On-demand task only, never auto-fires

Cloud Drives

Case Behavior
OAuth token expired Automatic refresh via stored refresh_token
Refresh token revoked Connection marked 'revoked', manual re-auth required
rclone process dies Mount health → 'degraded', auto-remount attempted
Network timeout RCLONE_RC_TIMEOUT_MS (default 60s), error returned
Large file download Streamed, no memory buffering
Concurrent mount operations rclone handles internally

Database

Case Behavior
Database corruption PRAGMA integrity_check on startup, warn in logs
Concurrent writes busy_timeout = 10000 waits up to 10s for lock
Database file missing Auto-created with full schema from init.sql
Migration failure Error logged, server may start in degraded state
RESET_DATABASE=true Full schema rebuild (destroys all data)

Admin Operations

Case Behavior
Delete only admin user TBD -- no protection against this currently
Delete user with active sessions CASCADE deletes session records, CLI process orphaned
Change own admin status TBD -- UI may not prevent self-demotion

Empty States

Screen Empty State
Chat Welcome message with suggested prompts
File Tree "No files" with upload button CTA
Skills Empty list with "Create skill" button
Schedules Empty list with "Create schedule" button
Meetings No active meeting indicator
Git "No repository" or "No changes"
Analytics "No data" with refresh button
Users (admin) First user always exists

Loading States

Screen Loading Pattern
Chat response Token-by-token streaming, animated dots
File tree Skeleton loader
Skill list Spinner
Schedule list Spinner
Analytics reports Section-by-section loading
Cloud file browsing Spinner with directory path
Onboarding research Step-by-step progress messages

Error States

Error User-Facing Message Recovery
Claude not configured "Claude is not configured" banner Setup form
API key invalid "Invalid API key" in chat Reconfigure in settings
Database error 500 error Restart container
File upload too large "File exceeds maximum size" Reduce file size
Network error "Connection lost" Auto-reconnect attempt
Permission denied (non-admin) 403 response Contact admin
WebSocket failure "Reconnecting..." banner Auto-retry

Race Conditions

Scenario Mitigation
Two users editing CLAUDE.md Last write wins (no locking)
Schedule fires during server shutdown Execution stays 'running', marked 'stale' on restart
Multiple file uploads to same path Last write wins (filesystem)
Concurrent API key validation bcrypt is thread-safe, no issue
Stats refresh triggered while running stats_refresh_log status check prevents duplicate
OAuth callback arrives after timeout Connection stays 'pending' until successful callback

Timezone Handling

  • Schedules: Stored timezone per schedule, evaluated by cron-parser
  • Timestamps: All database timestamps are UTC (ISO 8601 strings or CURRENT_TIMESTAMP)
  • Display: Client-side conversion to local timezone
  • Logs: UTC timestamps in JSONL files