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

  1. Inspect the keys printed in the message to see the unsupported operator/shape.
  2. If you need OR/NOT, extend the adapter to handle LOGICAL_OPERATOR.OR/NOT, or convert the payload to AND-only form upstream.
  3. 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

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


AI-assisted analysis of makeplane/plane@1c8a60f858 (2026-08-12). Data as JSON: /api/errors/17e1328b19cb0924. Report an issue: GitHub.