makeplane/plane · error · Error
Invalid expression: unknown structure with keys [${expressio
Error message
Invalid expression: unknown structure with keys [${expressionKeys.join(", ")}] What it means
Thrown at the end of _convertExpressionToInternal after the object is confirmed not to be a simple condition (no field key) and does not contain LOGICAL_OPERATOR.AND. The message lists the actual keys so the caller can see exactly what unrecognized shape was passed. Only AND groups and single conditions are supported by this adapter.
Source
Thrown at packages/shared-state/src/store/work-item-filters/adapter.ts:84
});
}
// It's a logical group - check which type
const expressionKeys = Object.keys(expression);
if (LOGICAL_OPERATOR.AND in expression) {
const andExpression = expression as { [LOGICAL_OPERATOR.AND]: TWorkItemFilterExpressionData[] };
const andConditions = andExpression[LOGICAL_OPERATOR.AND];
if (!Array.isArray(andConditions) || andConditions.length === 0) {
throw new Error("AND group must contain at least one condition");
}
const convertedConditions = andConditions.map((item) => this._convertExpressionToInternal(item));
return createAndGroupNode(convertedConditions);
}
throw new Error(`Invalid expression: unknown structure with keys [${expressionKeys.join(", ")}]`);
}
/**
* Converts internal filter expression to external format
* @param internalFilter - The internal filter expression
* @returns External filter expression
*/
toExternal(internalFilter: TFilterExpression<TWorkItemFilterProperty>): TWorkItemFilterExpression {
if (!internalFilter) {
return {};
}
try {
return this._convertExpressionToExternal(internalFilter);
} catch (error) {
console.error("Failed to convert internal filter to external:", error);
return {};
}View on GitHub (pinned to 1c8a60f858)
Solutions
- Inspect the keys printed in the message to see the unsupported operator/shape.
- If you need OR/NOT, extend the adapter to handle LOGICAL_OPERATOR.OR/NOT, or convert the payload to AND-only form upstream.
- For malformed single conditions, ensure each condition has the keys _isWorkItemFilterConditionData requires (field, operator/operator structure, value).
Example fix
// before
adapter.toInternal({ OR: [{ field: 'state', value: 'open' }] });
// after (flatten OR into an accepted shape, or extend the adapter)
adapter.toInternal({
AND: [
{ field: 'state', operator: 'is', value: 'open' },
],
}); Defensive patterns
Strategy: validation
Validate before calling
const KNOWN_TOP_KEYS = new Set(['field', 'AND']);
function isRecognizedShape(e: Record<string, unknown>): boolean {
const keys = Object.keys(e);
return keys.every(k => KNOWN_TOP_KEYS.has(k)) && (keys.includes('field') || keys.includes('AND'));
} Type guard
function isConditionOrAnd(e: any): boolean {
if (!e || typeof e !== 'object') return false;
return 'field' in e || ('AND' in e && Array.isArray(e.AND));
} Try / catch
try { adapter.toInternal(expr); } catch (e) {
if (/unknown structure with keys/.test((e as Error).message)) { logUnsupportedKeys(expr); expr = {}; } else throw e;
} Prevention
- Confirm only AND groups and supported conditions are emitted
- Extend the adapter when adding OR/NOT
- Validate payloads at the API boundary before storage
When it happens
Trigger: Passing an OR group ({ OR: [...] }) which the adapter does not implement; passing { field: 'x', value: 1 } missing the keys _isWorkItemFilterConditionData expects; passing arbitrary objects like { foo: 1 } or a NOT expression.
Common situations: Schema/version mismatch where another part of the app (or a newer backend) emits OR/NOT groups the adapter can't parse; hand-built test fixtures with wrong keys; copy-paste of a filter payload from a different filter system.
Related errors
- Invalid expression: empty or null data
- AND group must contain at least one condition
- Invalid relative amount: ${amountStr}
- Unsupported time unit: ${unit}
- Invalid date format: {value}
AI-assisted analysis of makeplane/plane@1c8a60f858 (2026-08-12).
Data as JSON: /api/errors/17e1328b19cb0924.
Report an issue: GitHub.