affaan-m/ECC · error · Error

Structured session targets require a non-empty type

Error message

Structured session targets require a non-empty type

What it means

Thrown by normalizeStructuredTarget in the session-adapter registry when a target is passed as an object (structured form) but its `type` field is missing, empty, or not a string. The type is required because TARGET_TYPE_TO_ADAPTER_ID maps it (plan, session, claude-history, claude-alias, session-file, codex-worktree, codex, opencode) to the adapter that should open the session.

Source

Thrown at scripts/lib/session-adapters/registry.js:63

  if (typeof value !== 'string' || value.trim().length === 0) {
    throw new Error('Structured session targets require a non-empty string value');
  }

  return value.trim();
}

function normalizeStructuredTarget(target, context = {}) {
  if (!target || typeof target !== 'object' || Array.isArray(target)) {
    return {
      target,
      context: { ...context }
    };
  }

  const value = coerceTargetValue(target.value);
  const type = typeof target.type === 'string' ? target.type.trim() : '';
  if (type.length === 0) {
    throw new Error('Structured session targets require a non-empty type');
  }

  const adapterId = target.adapterId || TARGET_TYPE_TO_ADAPTER_ID[type] || context.adapterId || null;
  const nextContext = {
    ...context,
    adapterId
  };

  if (type === 'claude-history' || type === 'claude-alias') {
    return {
      target: `claude:${value}`,
      context: nextContext
    };
  }

  if (type === 'codex-worktree' || type === 'codex') {
    return {
      target: `codex:${value}`,

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Add a non-empty `type` string from the supported set: plan, session, claude-history, claude-alias, session-file, codex-worktree, codex, or opencode.
  2. If the target is a plain string (e.g. 'claude:alias' or 'tmux:plan-name'), pass it directly — strings skip structured normalization entirely.
  3. Remember `type` is validated before `adapterId` is used, so setting adapterId alone is not enough; you still need a type on structured targets.

Example fix

// before
registry.open({ value: 'proj-session' });
// throws: Structured session targets require a non-empty type

// after
registry.open({ type: 'session', value: 'proj-session' });
Defensive patterns

Strategy: validation

Validate before calling

const VALID_TYPES = new Set(['plan','session','claude-history','claude-alias','session-file','codex-worktree','codex','opencode']);
function assertStructuredTarget(t) {
  if (t && typeof t === 'object' && !Array.isArray(t)) {
    if (typeof t.type !== 'string' || t.type.trim().length === 0) {
      throw new TypeError('target.type missing');
    }
    if (!VALID_TYPES.has(t.type.trim())) {
      console.warn('unknown target type:', t.type);
    }
  }
}

Type guard

function isStructuredTarget(t) {
  return t != null && typeof t === 'object' && !Array.isArray(t)
    && typeof t.type === 'string' && t.type.trim().length > 0;
}

Try / catch

try {
  registry.open(target);
} catch (err) {
  if (/non-empty type/.test(err.message)) { /* fix target.type */ }
  else throw err;
}

Prevention

When it happens

Trigger: Calling registry.select/open/normalizeStructuredTarget with `{ value: 'x' }` (no type), `{ value: 'x', type: ' ' }` (whitespace-only), or `{ value: 'x', type: 123 }` (non-string). Note the value field is checked first by coerceTargetValue, so a missing value throws a different error before this one.

Common situations: Building a structured target programmatically and forgetting the `type` key; renaming `type` to `kind`/`category`; passing a half-built config object; copy-pasting a target literal from docs that omitted type.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/1a63bbc339ca493f. Report an issue: GitHub.