Hmbown/CodeWhale · error · ExecError

open_application needs a valid Harmony bundle identifier

Error message

open_application needs a valid Harmony bundle identifier

What it means

open_application validates the bundle identifier against a strict Harmony pattern (dot-separated identifier segments, each starting with a letter/underscore, max 256 chars) before invoking `aa start`; anything else is rejected with this ExecError to prevent malformed arguments reaching the device shell.

Solutions

  1. Pass the exact Harmony bundle name as bundle_id (e.g. 'com.example.myapp'); list valid ids with `bm dump -a` via list_apps.
  2. Ensure either bundle_id or name is provided and is a non-empty dot-separated identifier.
  3. Pre-validate with the same regex: /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/ and length ≤ 256.
  4. Sanitize macOS/iOS identifiers (replace hyphens) or map app names to Harmony bundle ids before calling.

Example fix

// before
await backend.open_application({ bundle_id: 'My App' });
// after
await backend.open_application({ bundle_id: 'com.example.myapp' });
Defensive patterns

Strategy: validation

Validate before calling

const HARmony_ID = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/;
if (typeof bundle !== 'string' || bundle.length > 256 || !HARmony_ID.test(bundle)) throw new TypeError('invalid Harmony bundle identifier');

Type guard

const isHarmonyIdentifier = (v) => typeof v === 'string' && v.length <= 256 && /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(v);

Try / catch

try { await backend.open_application({ bundle_id: bid }); } catch (e) { if (String(e.message).includes('valid Harmony bundle identifier')) { const apps = await backend.list_apps(); /* resolve correct bundle id, retry */ } else throw e; }

Prevention

When it happens

Trigger: Calling open_application with a bundle_id/name that is undefined, empty, contains spaces/slashes/hyphens at segment starts, exceeds 256 chars, or is not a dot-separated identifier (e.g. 'My App', '/Applications/Safari.app', 'com.example-app').

Common situations: Passing macOS-style app names or .app paths to the Harmony backend; using display names instead of bundle ids; missing both bundle_id and name so bundle is undefined; hyphens from iOS-style reverse-DNS ids.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/e2cf1e1ff84c32db. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/plugins/computer-use/src/backends/harmonyos.mjs:185

    async switch_display({ index }) {
      if (index !== 1) throw new ExecError("harmony backend exposes display 1 only");
      return { activeDisplay: 1 };
    },
    list_apps: async () => {
      const out = await deviceOut(["bm", "dump", "-a"], { timeoutMs: 25_000 });
      const bundles = out.split("\n").map((s) => s.trim()).filter((s) => /^[a-zA-Z][\w.]*$/.test(s));
      return { apps: bundles.map((b) => ({ name: b, bundle_id: b, kind: "bundle" })) };
    },
    list_windows: async (args = {}) => {
      rejectAppSelectors(args);
      const out = await deviceOut(["hidumper", "-s", "WindowManagerService", "-a", "-a"], { timeoutMs: 25_000 }).catch(() => "");
      const windows = out.split("\n").filter((l) => /Window Name|bundleName/i.test(l)).slice(0, 40).map((l) => ({ title: l.trim().slice(0, 160) }));
      return { windows: windows.length ? windows : [{ title: "(window list unavailable on this HarmonyOS build)" }] };
    },
    open_application: async ({ bundle_id: bid, ability, name } = {}) => {
      const bundle = bid ?? name;
      const identifier = (value) => typeof value === "string" && value.length <= 256 && /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(value);
      if (!identifier(bundle)) throw new ExecError("open_application needs a valid Harmony bundle identifier");
      if (ability != null && !identifier(ability)) throw new ExecError("open_application needs a valid Harmony ability identifier");
      const candidates = ability != null ? [ability] : ["EntryAbility", "MainAbility"];
      let last = null;
      for (const a of candidates) {
        const r = await shell(["aa", "start", "-b", escDeviceText(bundle), "-a", escDeviceText(a)]);
        if (r.code === 0 && !/Error|error/.test(r.stdout + r.stderr)) {
          return { launched: true, bundle, ability: a };
        }
        last = (r.stderr || r.stdout).trim().slice(0, 200);
      }
      throw new ExecError(`aa start failed: ${last}`);
    },
    get_app_state: async (args = {}) => {
      rejectAppSelectors(args);
      const tree = await dumpLayout();
      const els = flatten(tree);
      return { bundle_id: tree.attributes?.bundleName ?? null, elements: els, truncated: els.length >= 600 };
    },

View on GitHub (pinned to 73e0f67d83)