nexu-io/open-design · error

${field} must be a dot-separated JSON path

Error message

${field} must be a dot-separated JSON path

What it means

parseMappingPath (refresh.ts:279-283) normalizes an outputMapping dataPath (stripping an optional '$.' prefix) and requires it to be a clean dot-separated path: non-empty, not starting or ending with '.', and containing no '..'. This is used for both dataPaths.from (read) and dataPaths.to (write).

Source

Thrown at apps/daemon/src/live-artifacts/refresh.ts:282

    || value === 'public_github_repository_metric';
}

function asBoundedRefreshOutput(value: BoundedJsonObject): BoundedJsonObject {
  const result = validateBoundedJsonObject(value, 'localRefreshOutput');
  if (!result.ok) {
    const firstIssue = result.issues[0];
    throw new Error(firstIssue === undefined ? result.error : `${firstIssue.path}: ${firstIssue.message}`);
  }
  return result.value;
}

const SAFE_MAPPING_SEGMENT = /^[A-Za-z_][A-Za-z0-9_-]*$|^(?:0|[1-9][0-9]*)$/;
const UNSAFE_MAPPING_SEGMENTS = new Set(['__proto__', 'prototype', 'constructor']);

function parseMappingPath(path: string, field: string): string[] {
  const normalized = path.startsWith('$.') ? path.slice(2) : path;
  if (normalized.length === 0 || normalized.startsWith('.') || normalized.endsWith('.') || normalized.includes('..')) {
    throw new Error(`${field} must be a dot-separated JSON path`);
  }
  const segments = normalized.split('.');
  for (const segment of segments) {
    if (!SAFE_MAPPING_SEGMENT.test(segment) || UNSAFE_MAPPING_SEGMENTS.has(segment)) {
      throw new Error(`${field} contains unsupported JSON path segment: ${segment}`);
    }
  }
  return segments;
}

function isJsonObject(value: BoundedJsonValue | undefined): value is BoundedJsonObject {
  return value !== null && typeof value === 'object' && !Array.isArray(value);
}

function readMappedValue(root: BoundedJsonObject, path: string): BoundedJsonValue | undefined {
  let current: BoundedJsonValue | undefined = root;
  for (const segment of parseMappingPath(path, 'outputMapping.dataPaths.from')) {
    if (Array.isArray(current)) {

View on GitHub (pinned to 5be4028344)

Solutions

  1. Fix the dataPath to be a clean dot-separated path with no leading/trailing/double dots, e.g. 'items.0.name'.
  2. Strip empty segments when building paths dynamically.
  3. Validate dataPaths with a regex/unit test before persisting the source.

Example fix

// before
outputMapping: { dataPaths: [{ from: 'items.', to: '.result' }] }
// throws `outputMapping.dataPaths.from must be a dot-separated JSON path`

// after
outputMapping: { dataPaths: [{ from: 'items', to: 'result' }] }
Defensive patterns

Strategy: validation

Validate before calling

function isValidDataPath(p: string): boolean {
  const norm = p.startsWith('$.') ? p.slice(2) : p;
  return norm.length > 0 && !norm.startsWith('.') && !norm.endsWith('.') && !norm.includes('..');
}
for (const dp of source.outputMapping?.dataPaths ?? []) {
  if (!isValidDataPath(dp.from) || !isValidDataPath(dp.to)) throw new Error('invalid dataPath');
}

Type guard

function isDotSeparatedPath(p: unknown): p is string {
  if (typeof p !== 'string') return false;
  const n = p.startsWith('$.') ? p.slice(2) : p;
  return n.length > 0 && !n.startsWith('.') && !n.endsWith('.') && !n.includes('..');
}

Prevention

When it happens

Trigger: An outputMapping.dataPaths entry has a `from` or `to` value that is '', '.', '.foo', 'foo.', or 'foo..bar'.

Common situations: Hand-edited or programmatically-built outputMapping that leaves an empty path, includes a trailing dot, or joins segments with a missing middle (producing '..'); copying a JSONPath like '$.foo.' verbatim.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/086d6af4ae68289b. Report an issue: GitHub.