Environment
- macOS 26.5 (build 25F71) — Apple silicon (Mac mini M-series)
- Xcode 26.5 (17F42)
- Swift 6.3.2 (swiftlang-6.3.2.1.108)
- Apple Development cert:
Apple Development: <email> (5X9WMS8T3G) — note: certificate subject is CN=…(5X9WMS8T3G)/OU=38Z85GV2D8, i.e. the trailing (…) in CN is not the team ID.
Reproduction
git clone https://github.com/steipete/CodexBar.git
cd CodexBar
./Scripts/compile_and_run.sh
# App launches, menu bar icon appears. Try to add a CodexBar widget — none in the gallery.
Bug 1 — Team ID parsed from wrong cert field
Scripts/compile_and_run.sh:88-97 extracts the team ID from the (...) suffix of the certificate's CN. For some Apple Development certificates that value is the Apple ID identifier, not the Team ID. Result: app and widget extension get signed with TeamIdentifier=<real_team> but their entitlements claim app group <wrong_team>.com.steipete.codexbar, which macOS rejects.
Reproducer: cert subject CN=Apple Development: …(5X9WMS8T3G)/OU=38Z85GV2D8/O=…/C=US. The script picks 5X9WMS8T3G; correct team ID is 38Z85GV2D8 (in OU).
Suggested fix: Read team ID from the certificate's OU field via security find-certificate -c "$identity" -p | openssl x509 -noout -subject, fall back to the existing CN-suffix parse only if OU lookup fails.
Workaround: set APP_TEAM_ID env var before running the script.
Bug 2 — Widget extension exits immediately on macOS 26 because WidgetBundle.main() does not block
After working around Bug 1, the widget extension is correctly signed and registered with pluginkit / chronod, but every getAllDescriptors query fails with:
chronod: (WidgetKit) [com.apple.chrono:session] Unexpected error on session:
Error Domain=NSCocoaErrorDomain Code=4099
"The connection to service with pid -1 named (null) was invalidated."
The widget extension is launched by chronod, logs progress, and then exits cleanly with status 0 (no crash report). Sequence from log show:
CodexBarWidget: (libsystem_secinit.dylib) AppSandbox
CodexBarWidget: (WidgetKit) [com.apple.chrono:widget] main [WidgetBundle]
CodexBarWidget: (WidgetKit) [com.apple.chrono:widget] Locale token: …
CodexBarWidget: (WidgetKit) [com.apple.chrono:widget] WidgetHost - Optional(WidgetKit.ResolvedWidgetBundleHost)
CodexBarWidget: (ExtensionFoundation) [com.apple.extensionkit:launch] Extension Type: '<private>' : <private>
runningboardd: termination reported by launchd (0, 0, 0)
lldb backtrace at exit()
With get-task-allow added to the widget entitlements and lldb -o "process attach --name CodexBarWidget --waitfor" plus a breakpoint on exit:
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x00000001866c42d0 libdyld.dylib`dyld4::LibSystemHelpers::exit(int) const
frame #1: 0x00000001866fbf0c dyld`dyld4::LibSystemHelpersWrapper::exit(int) const + 164
frame #2: 0x00000001866fbe30 dyld`start + 7040
Three frames — exit is being called from dyld's post-main epilogue. The user's main() returned with code 0. That is: SwiftUI's @main-generated entry point calls WidgetBundle.main(), that returns, and the process exits without entering an XPC runloop. On macOS 14/15 this presumably worked because ExtensionFoundation set up a blocking runloop internally; on macOS 26 it does not.
Things ruled out
| Tried |
Result |
NSExtension+NSExtensionPrincipalClass (current default) |
Crashes _EXRunningExtension with Fatal error: Unrecognized extension type |
NSExtension (no PrincipalClass) |
Same Unrecognized extension type crash |
EXAppExtensionAttributes { EXExtensionPointIdentifier } |
Past the crash, but now exits cleanly without serving descriptors |
Both NSExtension and EXAppExtensionAttributes |
Same — exits cleanly, no descriptors |
LSMinimumSystemVersion = 26.0 |
No change |
Strip bundle to one StaticConfiguration widget |
Same failure — not widget-specific |
CODEXBAR_WIDGET_METADATA_MODE=required (generates Metadata.appintents) |
No change |
Swap in xcodebuild-built CodexBarWidget binary instead of swift build one |
No change — both produce equivalent binaries because CodexBarWidget is .executableTarget, not an extension target |
Root cause
Package.swift defines CodexBarWidget as .executableTarget. Both swift build and xcodebuild -workspace .swiftpm/xcode/package.xcworkspace -scheme CodexBarWidget produce the same plain executable; Scripts/package_app.sh then hand-wraps it into an .appex bundle. SwiftPM has no first-class notion of a "widget extension target," so neither path applies the extension-specific linker/entry-point setup that Xcode's WidgetKit template provides. On macOS 26 that template setup is what supplies the blocking runloop.
Suggested fix direction
Add a real Xcode Widget Extension target (com.apple.product-type.app-extension) that compiles Sources/CodexBarWidget/**/*.swift (and the shared CodexBarCore module), and have package_app.sh use the .appex it produces rather than the SwiftPM-built executable. The existing xcodebuild call for App Intents metadata could be the same invocation.
Happy to test fixes — I'm on macOS 26.5 and can iterate.
Additional details
Scripts/compile_and_run.sh:88-97 (Bug 1 location)
export_team_id_from_identity() {
local identity="${1:-}"
if [[ -n "${APP_TEAM_ID:-}" || -z "${identity}" ]]; then
return
fi
if [[ "${identity}" =~ \(([A-Z0-9]{10})\)$ ]]; then
APP_TEAM_ID="${BASH_REMATCH[1]}"
export APP_TEAM_ID
fi
}
Environment
Apple Development: <email> (5X9WMS8T3G)— note: certificate subject isCN=…(5X9WMS8T3G)/OU=38Z85GV2D8, i.e. the trailing(…)in CN is not the team ID.Reproduction
Bug 1 — Team ID parsed from wrong cert field
Scripts/compile_and_run.sh:88-97extracts the team ID from the(...)suffix of the certificate's CN. For some Apple Development certificates that value is the Apple ID identifier, not the Team ID. Result: app and widget extension get signed withTeamIdentifier=<real_team>but their entitlements claim app group<wrong_team>.com.steipete.codexbar, which macOS rejects.Reproducer: cert subject
CN=Apple Development: …(5X9WMS8T3G)/OU=38Z85GV2D8/O=…/C=US. The script picks5X9WMS8T3G; correct team ID is38Z85GV2D8(in OU).Suggested fix: Read team ID from the certificate's OU field via
security find-certificate -c "$identity" -p | openssl x509 -noout -subject, fall back to the existing CN-suffix parse only if OU lookup fails.Workaround: set
APP_TEAM_IDenv var before running the script.Bug 2 — Widget extension exits immediately on macOS 26 because
WidgetBundle.main()does not blockAfter working around Bug 1, the widget extension is correctly signed and registered with
pluginkit/ chronod, but everygetAllDescriptorsquery fails with:The widget extension is launched by chronod, logs progress, and then exits cleanly with status 0 (no crash report). Sequence from
log show:lldb backtrace at
exit()With
get-task-allowadded to the widget entitlements andlldb -o "process attach --name CodexBarWidget --waitfor"plus a breakpoint onexit:Three frames — exit is being called from dyld's post-main epilogue. The user's
main()returned with code 0. That is: SwiftUI's@main-generated entry point callsWidgetBundle.main(), that returns, and the process exits without entering an XPC runloop. On macOS 14/15 this presumably worked becauseExtensionFoundationset up a blocking runloop internally; on macOS 26 it does not.Things ruled out
NSExtension+NSExtensionPrincipalClass(current default)_EXRunningExtensionwithFatal error: Unrecognized extension typeNSExtension(no PrincipalClass)Unrecognized extension typecrashEXAppExtensionAttributes { EXExtensionPointIdentifier }NSExtensionandEXAppExtensionAttributesLSMinimumSystemVersion = 26.0StaticConfigurationwidgetCODEXBAR_WIDGET_METADATA_MODE=required(generatesMetadata.appintents)CodexBarWidgetbinary instead ofswift buildoneCodexBarWidgetis.executableTarget, not an extension targetRoot cause
Package.swiftdefinesCodexBarWidgetas.executableTarget. Bothswift buildandxcodebuild -workspace .swiftpm/xcode/package.xcworkspace -scheme CodexBarWidgetproduce the same plain executable;Scripts/package_app.shthen hand-wraps it into an.appexbundle. SwiftPM has no first-class notion of a "widget extension target," so neither path applies the extension-specific linker/entry-point setup that Xcode's WidgetKit template provides. On macOS 26 that template setup is what supplies the blocking runloop.Suggested fix direction
Add a real Xcode
Widget Extensiontarget (com.apple.product-type.app-extension) that compilesSources/CodexBarWidget/**/*.swift(and the shared CodexBarCore module), and havepackage_app.shuse the.appexit produces rather than the SwiftPM-built executable. The existing xcodebuild call for App Intents metadata could be the same invocation.Happy to test fixes — I'm on macOS 26.5 and can iterate.
Additional details
Scripts/compile_and_run.sh:88-97(Bug 1 location)