eyaltoledano/claude-task-master · error · TaskMasterError

NO_BRIEF_SELECTED

NO_BRIEF_SELECTED

Error message

No brief context found for watching. Please connect to a brief first.

What it means

TaskMasterError with code NO_BRIEF_SELECTED thrown by ApiStorage.watch when AuthManager's context has no briefId. Real-time watching is scoped to a brief, so without a connected brief the adapter refuses to start a watcher instead of watching nothing.

Source

Thrown at packages/tm-core/src/modules/storage/adapters/api-storage.ts:965

	/**
	 * Watch for changes to tasks via Supabase Realtime
	 * Subscribes to postgres_changes for the tasks table filtered by brief_id
	 *
	 * Optional debouncing is supported for collaborative scenarios where multiple
	 * rapid changes (e.g., bulk operations, multiple users) could cause UI flicker.
	 * Default: 300ms (higher than FileStorage's 100ms since Realtime events are cleaner)
	 */
	async watch(
		callback: (event: WatchEvent) => void,
		options?: WatchOptions
	): Promise<WatchSubscription> {
		await this.ensureInitialized();

		const authManager = AuthManager.getInstance();
		const context = authManager.getContext();

		if (!context?.briefId) {
			throw new TaskMasterError(
				'No brief context found for watching. Please connect to a brief first.',
				ERROR_CODES.NO_BRIEF_SELECTED,
				{ operation: 'watch' }
			);
		}

		const supabase = authManager.supabaseClient.getClient();
		const channelName = `tasks-watch-${context.briefId}-${Date.now()}`;
		const debounceMs = options?.debounceMs ?? 300;

		let debounceTimer: NodeJS.Timeout | undefined;
		let closed = false;

		const debouncedCallback = () => {
			if (closed) return;
			if (debounceTimer) {
				clearTimeout(debounceTimer);
			}

View on GitHub (pinned to c0c98d367c)

Solutions

  1. Connect to a brief first (the auth/connect flow that populates AuthManager context with briefId), then call watch.
  2. Check AuthManager.getInstance().getContext()?.briefId before calling watch and short-circuit with a friendly prompt.
  3. Re-authenticate if the session expired and the brief context was lost.
  4. In CI/automation, ensure brief selection is configured via env/config before any watch call.

Example fix

// before
const watcher = await storage.watch(cb); // throws NO_BRIEF_SELECTED
// after
const ctx = AuthManager.getInstance().getContext();
if (!ctx?.briefId) {
  await connectToBrief(briefId); // sets auth context
}
const watcher = await storage.watch(cb);
Defensive patterns

Strategy: validation

Validate before calling

import { AuthManager } from '@tm/core';
const ctx = AuthManager.getInstance().getContext();
if (!ctx?.briefId) {
  throw new Error('No brief connected. Run the connect flow before watch().');
}

Type guard

function hasBriefContext(ctx: { briefId?: string } | null | undefined): ctx is { briefId: string } {
  return typeof ctx?.briefId === 'string' && ctx.briefId.length > 0;
}

Try / catch

try {
  const watcher = await storage.watch(onChange);
} catch (e) {
  if ((e as TaskMasterError).code === 'NO_BRIEF_SELECTED') {
    await promptBriefSelection(); // interactive or env-based connect
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling storage.watch() (or watch on a collection) before authenticating, after authentication but without connecting/selecting a brief, or after the stored auth context was cleared/expired so context?.briefId is undefined.

Common situations: CLI/automation started fresh without a prior 'connect to brief' step, CI job missing the brief-selection env/config, a logged-out or expired session dropping the brief context, or running watch in a code path that never sets up AuthManager context.

Related errors


AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29). Data as JSON: /api/errors/8c3f99603032cd14. Report an issue: GitHub.