Troubleshooting
Every ipa-forge error message mapped to its cause and fix.
Errors are reported as single-line error: messages (CLI) or a red banner
(GUI) — never raw tracebacks. This page maps each message to its cause and fix.
Hook verification reports "missing" / "elsewhere"
Symptom
forge patch --dry-run or forge hooks verify reports a hook with
missing-class, missing-selector, or elsewhere. A referenced-only
status means the selector is referenced by the binary but not declared as a
method anywhere — there is no IMP to swizzle, so the hook cannot attach no
matter what class you point it at (double-check with forge hooks find <selector> --ipa App.ipa before porting it).
Cause: the app version doesn't have the class/selector the tweak targets —
a silent no-op on device (feature stops working, no crash). This is exactly
what the hooks: block exists to catch.
Fix: find the new name: forge hooks extract --ipa App.ipa --search "<substring of old class>" shows what replaced it (e.g. PlaybackController
→ PlaybackControllerImpl). Update the hook target (and the tweak
source), then re-run --dry-run. Statuses unverified and ok-inherited
are soft — the class/selector likely exists but the parser couldn't fully
confirm (system superclasses, chained-fixup gaps) — verify once on device
via your tweak's attach logs rather than chasing them.
A feature does nothing on device, but --dry-run and hooks verify are green
Symptom
The tweak source clearly calls a hook, forge patch --dry-run shows
0 issue(s), everything looks fine — and the feature still silently
doesn't work on device.
Cause: the hook was never added to the hooks: block. --dry-run only
verifies declared hooks; a hook the source calls but the definition never
mentions isn't checked at all, isn't reported as missing, and isn't
reported as anything — it's simply absent from the report, which is easy to
misread as "nothing to worry about."
Fix
forge hooks audit --ipa App.ipa --dir <dylib-sources> --patches patch.yaml scans the source independently of the definition and diffs the
two, printing every hook the source calls that isn't declared (exit 1 if
any). This is a real, recurring mistake, not a hypothetical one. Add the
missing hook(s), re-run --dry-run to confirm they attach, and make
hooks audit --patches a standard part of the pre-commit loop from then on
(see Patch Reference, "The hooks block" section).
Patch definition problems
patch definition '<file>' is invalid: ...
The YAML parsed but failed schema validation (missing target, unknown
type, missing required field, empty patches: list, plist_edit set
without a value, etc.). The message names the failing field(s). Fix the
definition per the Patch Reference.
patch definition '<file>' is not valid YAML: ...
The file isn't parseable YAML — check quoting/indentation. (YAML and JSON are both accepted.)
patch definition '<file>' matches 0 patch operations for <bundle> v<version> (definition targets <other>); nothing will be applied
The target block doesn't match this IPA. Check bundle_id (typo?) and the
version rule (exact is string equality — "1.0.0" won't match
"1.0.0-beta"; max is exclusive).
- In
--dry-runthis is a warning (handy for confirming a version gate). - In a real run it's a hard error:
refusing to produce an unpatched IPA.
patch dry-run validation failed: <op_id>: <message>
The dry-run gate rejected the patch — nothing was modified. Look up the message:
Dylib injection specifics
INJECTION_SKIPPED: <name> is already loaded— the load command already exists; the operation is a no-op (not an error).INJECTION_UNSUPPORTED: ... is not a valid Mach-O file— wrongexecutable.- Remember
dylib_injectonly adds the load command — the dylib file must already be in the bundle (pair it with aresource_add). An injected but unstaged dylib passes the pipeline but fails at runtime on device.
Signing problems
no codesigning identity matches '<query>' / '<query>' matches multiple ...
The --identity substring doesn't uniquely match anything in
security find-identity -v -p codesigning. Use the full SHA-1, a more
specific substring, or a valid name.
provisioning profile '<name>' (<uuid>) expired on <date>
The profile is expired — obtain a fresh one (Xcode or AltServer's pairing).
provisioning profile '<name>' authorizes '<pattern>', not '<bundle_id>'
The profile's application-identifier (team prefix stripped) doesn't cover
this bundle id. Use a matching profile, or a wildcard (TEAMID.*) profile.
no supplied provisioning profile authorizes bundle id '<id>' (available: ...)
Multiple profiles supplied, none of them match this target's bundle id.
Supply a matching --profile for it.
multiple supplied profiles authorize '<id>': ... / multiple supplied wildcard profiles: ...
Two profiles claim the same bundle id (or two wildcards) — remove the duplicate so selection is unambiguous.
profile '<name>' does not authorize '<bundle_id>' but will be embedded anyway (single-profile mode)
Warning only: one profile is signing everything, including an extension it doesn't authorize. Install may fail on device — supply a matching profile for that extension.
codesign failed for <target>: ... / codesign verification failed for <target>: ...
The signing stage failed. Common causes: expired identity, revoked
certificate, entitlements the profile doesn't authorize, or a corrupted
extraction. Check the codesign stderr in the message.
CLI / environment
error: --identity is required unless --dry-run is set / error: at least one --profile is required unless --dry-run is set
A real (non-dry-run) patch needs both. Add them, or add --dry-run to only
validate.
<path> is not a valid zip archive / IPA does not contain a Payload/ directory / Expected exactly one Payload/*.app, found N
The input isn't a standard-structure IPA. ipa-forge requires exactly one
Payload/<App>.app — re-export from Xcode or re-download the IPA.
GUI problems
identity is required unless dry_run is set/at least one provisioning profile is required unless dry_run is set(400) — uncheck "Dry run only" and fill the signing fields, or tick it to validate without signing.- Zip upload errors — the zip must contain exactly one top-level
.yaml/.yml/.json; entries escaping the extraction directory are rejected (zip-slip guard); oversized archives are refused. - Download link missing — dry runs produce no artifact, so there is no download button.
- The GUI binds
127.0.0.1only. Ifforge guifails to start with "address already in use", another instance (or an older build) is on that port — stop it or pass--port.
Debugging tips
Confirm the target. forge inspect <ipa> — confirm bundle id/version and
executable names before writing a definition.
Read the full manifest. forge patch ... --dry-run --verbose shows every
operation's status and message without touching anything.
Check per-operation messages. The manifest's patches_applied — each
entry carries the operation's own message (e.g. match offsets).
Confirm signing inputs. security find-identity -v -p codesigning and
security cms -D -i <profile>.
Device install failures. Follow the manual checklist in Device Testing.