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. PlaybackControllerPlaybackControllerImpl). 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-run this 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 — wrong executable.
  • Remember dylib_inject only adds the load command — the dylib file must already be in the bundle (pair it with a resource_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.1 only. If forge gui fails 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.

On this page