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
- Add a valid `type` string to the structured target, e.g. { type: 'claude', value: '...' }.
- Trim and check the type before calling: if (!target?.type?.trim()) throw/fallback before invoking select/open.
- If the value came from persisted JSON, fix the producer/serializer so the type key is emitted, or migrate old records to include it.
- 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
- Always construct targets through a factory helper that requires type at build time.
- Validate deserialized target JSON against a schema (e.g. z.object({ type: z.string().min(1), value: z.unknown() })) before use.
- Never map form/user input straight into target.type without trimming and checking.
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
- No session adapter matched target
- Template content must be a non-empty string
- Template name must be a non-empty string
- Unknown session adapter
- -32602
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)