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

normalizeStructuredTarget validates structured session targets before dispatching them to an adapter. A structured target object must carry a non-empty `type` string, which the registry uses to map the target to the correct session adapter via TARGET_TYPE_TO_ADAPTER_ID. When the type is missing, empty, or only whitespace, the registry cannot determine which adapter should handle the target, so it throws immediately.

Solutions

  1. Add a valid `type` string to the structured target, e.g. { type: 'claude', value: '...' }.
  2. Trim and check the type before calling: if (!target?.type?.trim()) throw/fallback before invoking select/open.
  3. If the value came from persisted JSON, fix the producer/serializer so the type key is emitted, or migrate old records to include it.
  4. If the target is genuinely a plain string, pass the string directly instead of wrapping it as { value: ... }.

Example fix

// before
registry.select({ value: 'session-123' });

// after
registry.select({ type: 'claude', value: 'session-123' });
Defensive patterns

Strategy: validation

Validate before calling

function isValidStructuredTarget(t) {
  return t && typeof t === 'object' && typeof t.type === 'string' && t.type.trim().length > 0 && 'value' in t;
}
if (!isValidStructuredTarget(target)) throw new Error('target.type must be a non-empty string');

Type guard

const isStructuredTarget = (t) => typeof t === 'object' && t !== null && typeof t.type === 'string' && t.type.trim() !== '';

Try / catch

try {
  adapter = registry.select(target, ctx);
} catch (err) {
  if (err.message.includes('non-empty type')) {
    adapter = registry.select({ type: 'claude', value: target.value }, ctx);
  } else throw err;
}

Prevention

When it happens

Trigger: Calling registry.select({ value: '...' }) or registry.open({ value: '...' }) with a structured target object that omits the `type` key, sets type: '' , type: ' ' (whitespace-only), or sets type to a non-string such as null, a number, or undefined.

Common situations: Deserializing targets from JSON where the type field was dropped or renamed (e.g. 'kind' instead of 'type'); building targets programmatically from user input where an empty form field becomes an empty string; copying examples that pass a plain string target vs a structured object and mixing the two shapes.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/1a63bbc339ca493f. Report an issue: GitHub.

Appendix: 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 8321021c54)