Hmbown/CodeWhale · error · ExecError
aa start failed
Error message
aa start failed: ${last} What it means
After trying each candidate ability with `aa start`, open_application throws this ExecError containing the last device-side error output (up to 200 chars) when no candidate launches successfully. It means the bundle/ability exists syntactically but the device refused to start it.
Solutions
- Read the embedded device output for the real cause ('Ability does not exist', 'install failed', etc.).
- Verify the bundle is installed via list_apps (`bm dump -a`) and use an exact bundle id.
- Find the app's real ability name (module.json5) and pass it explicitly as ability.
- Retry after confirming the device screen is unlocked; reboot the device or restart hdc if the error persists.
Example fix
try {
await backend.open_application({ bundle_id: 'com.example.app' });
} catch (e) {
if (String(e.message).startsWith('aa start failed')) {
const apps = await backend.list_apps();
const real = apps.apps.find(a => a.bundle_id.includes('example'));
if (real) await backend.open_application({ bundle_id: real.bundle_id });
} else throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
const apps = await backend.list_apps();
if (!apps.apps.some(a => a.bundle_id === bid)) console.warn(`bundle ${bid} not installed on device`); Type guard
null
Try / catch
try { await backend.open_application({ bundle_id: bid }); } catch (e) { if (String(e.message).startsWith('aa start failed')) { console.error('device said:', e.message); /* verify install + ability name, then retry */ } else throw e; } Prevention
- Confirm the bundle is installed via list_apps before launching.
- Parse the embedded device error text for the precise cause.
- Prefer default-ability launch (omit ability) unless the app needs a specific entry point.
- Keep the device unlocked and hdc healthy before launch attempts.
When it happens
Trigger: `aa start -b <bundle> -a <ability>` exits non-zero or prints Error for every candidate ability (EntryAbility, MainAbility or the explicit one) — e.g. the app is not installed, the ability name does not exist for that bundle, or the device refuses the launch.
Common situations: Typo'd bundle id for an app that is not installed on the device; app's actual ability class differs from EntryAbility/MainAbility; app disabled or device policy blocking `aa start`; stale device state requiring reboot.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- hdc shell exited
- clipboard read is not exposed by hdc on current HarmonyOS…
- clipboard write is not exposed by hdc on current HarmonyOS…
- cursor position does not exist on touch devices
- element_stale — re-run get_app_state; uitest indexes change…
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/8c2c84e04c74e8f2.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/backends/harmonyos.mjs:196
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 };
},
screenshot: async (args = {}) => {
rejectAppSelectors(args);
const { path: outPath } = args;
const dir = process.env.CODEWHALE_CU_RECORDINGS_DIR || path.join(os.homedir(), ".codewhale-cu", "recordings");
fs.mkdirSync(dir, { recursive: true });
const file = outPath || path.join(dir, `shot-${new Date().toISOString().replace(/[:.]/g, "-")}-${crypto.randomBytes(3).toString("hex")}.jpeg`);
await snapshot(file);
const buf = fs.readFileSync(file);
displayPixels = jpegSize(buf) ?? displayPixels;
return { file, bytes: buf.length, pixels: jpegSize(buf), scale: 1, points: jpegSize(buf) };
},View on GitHub (pinned to 73e0f67d83)