tinyhumansai/openhuman · error · SpecError

paths must be repo-relative, not absolute (got "${p}")

Error message

paths must be repo-relative, not absolute (got "${p}")

What it means

Thrown by validateSpec() in scripts/agent-batch/lib.mjs while validating an agent-batch spec. Each agents[].owned_paths entry must be a repo-relative directory prefix; an entry starting with '/' is rejected because ownership tracking and the findOverlaps() collision check operate on paths relative to the repo root, where an absolute path would never match and would silently break disjointness guarantees.

Source

Thrown at scripts/agent-batch/lib.mjs:160

        `${at}.owned_paths`,
      );
    }
    for (let j = 0; j < agent.owned_paths.length; j++) {
      const p = agent.owned_paths[j];
      if (typeof p !== "string" || p.length === 0) {
        throw new SpecError(
          "must be a non-empty string",
          `${at}.owned_paths[${j}]`,
        );
      }
      if (p.includes("*") || p.includes("?")) {
        throw new SpecError(
          `globs not allowed — use directory prefixes (got "${p}")`,
          `${at}.owned_paths[${j}]`,
        );
      }
      if (p.startsWith("/")) {
        throw new SpecError(
          `paths must be repo-relative, not absolute (got "${p}")`,
          `${at}.owned_paths[${j}]`,
        );
      }
    }
    if ("allowed_shared_paths" in agent) {
      if (!Array.isArray(agent.allowed_shared_paths)) {
        throw new SpecError("must be an array", `${at}.allowed_shared_paths`);
      }
    }
    if ("labels" in agent && !Array.isArray(agent.labels)) {
      throw new SpecError("must be an array", `${at}.labels`);
    }
  }
  return spec;
}

// Pure overlap detection: returns an array of collisions. Each entry is

View on GitHub (pinned to a221052e0d)

Solutions

  1. Strip the repo-root prefix from the entry so it is relative, e.g. "/home/me/openhuman/src/agent" → "src/agent"
  2. If you generated the spec programmatically, re-generate it with paths relative to the repo root (path.relative(repoRoot, p)) and re-run
  3. Confirm no other owned_paths entries have the same problem — validation stops at the first offender, so fix and re-run until the spec passes

Example fix

// spec.json — before
"owned_paths": ["/home/me/openhuman/src/agent"]

// after
"owned_paths": ["src/agent"]
Defensive patterns

Strategy: validation

Validate before calling

// Run before handing the spec to any agent-batch command
import { readFileSync } from "node:fs";
const spec = JSON.parse(readFileSync("batch.json", "utf8"));
for (const [i, agent] of spec.agents.entries()) {
  for (const [j, p] of agent.owned_paths.entries()) {
    if (typeof p !== "string" || p.length === 0) throw new Error(`agents[${i}].owned_paths[${j}]: empty`);
    if (p.startsWith("/")) throw new Error(`agents[${i}].owned_paths[${j}]: absolute path ${p}`);
  }
}

Type guard

function isRepoRelativePath(p) {
  return typeof p === "string" && p.length > 0 && !p.includes("*") && !p.includes("?") && !p.startsWith("/");
}

Try / catch

try {
  validateSpec(spec);
} catch (e) {
  if (e instanceof SpecError) {
    console.error(`spec invalid at ${e.path}: ${e.message}`);
    process.exit(2);
  }
  throw e;
}

Prevention

When it happens

Trigger: A spec JSON where any agents[i].owned_paths[j] string starts with '/', e.g. "/home/user/openhuman/src/agent" or "/Users/me/repo/docs". validateSpec() is called by every agent-batch subcommand (launch/status) right after loadSpec(), so the very first run against such a spec fails at agents[i].owned_paths[j] with the offending path echoed.

Common situations: Authors generate the spec from shell output ($PWD-prefixed paths), paste paths from an editor's 'copy absolute path' action, or port a spec from a machine-specific CI layout. The check sits directly after the glob check, so 'src/*' fails first with a different message; only clean-but-absolute paths reach this throw.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16). Data as JSON: /api/errors/b8d73fbc03b56574. Report an issue: GitHub.