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
- Pass the exact Harmony bundle name as bundle_id (e.g. 'com.example.myapp'); list valid ids with `bm dump -a` via list_apps.
- Ensure either bundle_id or name is provided and is a non-empty dot-separated identifier.
- Pre-validate with the same regex: /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/ and length ≤ 256.
- 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
- Source bundle ids from list_apps, never from display names or .app paths.
- Validate identifiers with the same regex the backend uses before calling.
- Require bundle_id explicitly instead of relying on the name fallback.
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
- invalid_target
- open_application needs a valid Harmony ability identifier
- unsupported key " " on harmony backend
- 1
- A pinned task provider requires an explicit model
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)