sinelaw/fresh · error

${built.error}

Error message

${built.error}

What it means

In the orchestrator plugin's create-agent path, the helper that builds a workspace spec from caller-supplied options (remote host, path, identity, extra args) returns a result object; if `built.ok` is false, its `built.error` message is thrown. This is the programmatic counterpart of the create dialog: the dialog keeps itself open and shows the message, while a scripted/API caller gets the same message as a rejected promise. Typical causes are invalid remote host/identity strings or bad extra args.

Solutions

  1. Read the thrown `built.error` message — it is the same text the dialog would show — and fix the offending option field (host/path/identity/extraArgs).
  2. Validate options before calling: ensure host is non-empty and well-formed, identity points to an existing key file.
  3. Use the interactive create dialog once to see which values it accepts, then mirror them in the scripted call.
  4. If extraArgs are rejected, split or remove the flags the builder does not allow.

Example fix

// before
await orchestrator.create({ host: "", path: "/srv/app" });
// after
if (!options.host) throw new Error("host is required for remote create");
await orchestrator.create({ host: "deploy@prod-1", path: "/srv/app" });
Defensive patterns

Strategy: validation

Validate before calling

function validateCreateOptions(o) {
  const errs = [];
  if (o.host !== undefined && !/^\S+@?\S+$/.test(String(o.host))) errs.push("host must be [user@]hostname");
  if (o.identity && !fs.existsSync(o.identity)) errs.push(`identity key not found: ${o.identity}`);
  if (errs.length) throw new Error("invalid create options: " + errs.join("; "));
}
validateCreateOptions(options); // call before orchestrator create

Type guard

function isValidHost(h) { return typeof h === "string" && h.trim().length > 0 && !/\s/.test(h.trim()); }

Try / catch

try {
  const spec = await orchestrator.buildSpec(options);
} catch (e) {
  // message equals what the dialog would show — surface it to the user for correction
  showCreateDialogWithPrefill(options, String(e.message));
}

Prevention

When it happens

Trigger: Calling the orchestrator's create/run API (e.g. runRemoteCreate or equivalent) with options whose remote host, path, identity, or extraArgs fail spec validation in the shared builder — such as an empty or malformed host, or extraArgs that the builder rejects.

Common situations: Scripts/automation constructing agent-run options by hand and passing a typo'd or empty `host`; SSH identity paths that don't exist; flag strings in extraArgs the builder's validation refuses; versions where the builder gained new validation rules the caller doesn't satisfy.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13). Data as JSON: /api/errors/0125ce6be9b25d0b. Report an issue: GitHub.

Appendix: source

Thrown at crates/fresh-editor/plugins/orchestrator.ts:10419

  if (options.backend === "ssh") {
    const raw = options.sshOptions;
    const extraArgs = Array.isArray(raw)
      ? raw.map((a) => String(a).trim()).filter((a) => a !== "")
      : trimmed(raw)
        ? trimmed(raw).split(/\s+/)
        : [];
    const built = buildSshSpec({
      ...agentOptions,
      host: trimmed(options.host),
      name,
      cmd,
      remotePath: trimmed(options.path),
      identity: trimmed(options.identity),
      extraArgs,
    });
    // The dialog keeps itself open on a bad host and shows the message; a
    // caller gets the same message as a thrown error.
    if (!built.ok) throw new Error(built.error);
    return built.spec;
  }

  const requestedPath = trimmed(options.path);
  const projectPath = requestedPath || localProjectDefault();
  // A path the caller passed has to exist. The dialog's field completes against
  // the filesystem, so a human sees a wrong path immediately; a caller passing
  // `path` by hand does not, and the create would otherwise "succeed" into a
  // workspace rooted at a directory that isn't there.
  if (requestedPath && !editor.fileExists(editor.localPath(requestedPath))) {
    throw new Error(`project path does not exist: ${requestedPath}`);
  }
  return buildLocalSpec({
    ...agentOptions,
    projectPath,
    // "" ⇒ `runLocalCreate` allocates the next `<project>-N` name, the same
    // default the dialog's placeholder shows.
    name,

View on GitHub (pinned to 67894ca546)