Layout recursion error message

Hi all,

when I launch my macOS app from Xcode 16 on ARM64, appKit logs me this error on the debug console:

It's not legal to call -layoutSubtreeIfNeeded on a view which is already being laid out. If you are implementing the view's -layout method, you can call -[super layout] instead. Break on _NSDetectedLayoutRecursion(void) to debug. This will be logged only once. This may break in the future.

_NSDetectedLayoutRecursion doesn't help a lot, giving me these assembly codes from a call to a subclassed window method that looks like this:

-(void) setFrame:(NSRect)frameRect display:(BOOL)flag {
    if (!_frameLocked) [super setFrame:frameRect display:flag];
}

I have no direct call to -layoutSubtreeIfNeeded from a -layout implementation in my codes. I have a few calls to this method from update methods, however even if I comment all of them, the error is still logged...

Finally, apart from that log, I cannot observe any layout error when running the program. So I wonder if this error can be safely ignored?

Thanks!

Answered by DTS Engineer in 883290022

I don’t have any good input on the original issue that kicked off this thread, but I wanted to address this:

if someone knows a good solution to disable all system logs while keeping my own

Xcode 15 introduced a new Console area with excellent integration with the system log. For example, consider this program:

import Foundation
import Network
import os.log

let log = Logger(subsystem: "", category: "")

func main() {
    let c = NWConnection(host: "example.com", port: 80, using: .tls)
    c.stateUpdateHandler = { newState in
        log.log("did change state, state: \( "\(newState)" )")
    }
    c.start(queue: .main)
    log.log("will enter main loop")
    dispatchMain()
}

main()

IMPORTANT This is not meant to work. It tries to connect to the HTTP port using TLS, which won’t end well. The goal is to generate a bunch of log entries from Network framework, which it absolutely does (-:

When I run this (Xcode 26.4 on macOS 26.3.1) I see this:

will enter main loop
did change state, state: preparing
quic_protector_key_update unsupported TLS ciphersuite: 0
… lots of stuff …
nw_endpoint_flow_failed_with_error [C1.1.1 mask.icloud.com:443 cancelled intermediate-flow ((null))] already failing, returning
did change state, state: waiting(-9836: bad protocol version)

It’s hard to distinguish my log entries from all the stuff generated by Network framework. To focus on just my stuff, I pasted library:Test806471 into the filter box at the bottom of the Console area (where Test806471 is the name of my tool). After that I see just this:

will enter main loop
did change state, state: preparing
did change state, state: waiting(-9836: bad protocol version)

Nice!


For lots more hints and tips on how to use the system log effectively, see Your Friend the System Log.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

There's no easy answer here. I wouldn't necessarily ignore the error. There may be one or more UI interactions that you simply haven't tried yet.

For example, I recently discovered that if I minimized a window and then restore it, I would get some auto layout errors. I fixed those. But the point is that the window/view controller/view/layout architecture is extremely complicated.

These kinds of errors may indicate that you're doing something in the wrong sequence, or doing something else that's invalid in some way. You're running on Xcode 16. It might not work on Xcode 26 or macOS 26. Or maybe macOS 26.1 or 26.2 breaks it.

Are you subclassing NSWindow? That is the kind of thing that will get you into trouble. Apple's APIs aren't like Perl. There isn't "more than one way to do it". Rather, on average, there's about 0.92 ways to do it.

Thanks for your answer. Yes I'm subclassing NSWindow: the method -setFrame:display: reproduced above is one of the NSWindow methods specialized by this subclass. But, apart of that, the subclass doesn't interfere with the layout system in no way.

You're right about new macOS versions, I definitely intend to test macOS 26 before publishing a new version of the app (I'm developing on Xcode16 because my deployment target is macOS 10.13).

But it does interfere. It blocks the operation if _frameLocked == true. I understand this seems pretty simple, but there's no way to know what the impacts of that might be. There are many different setFrame variants. I'm not implying that this is a problem, or in any way related to the message you're asking about. It's just example of the risks of interfering in a really fragile architecture.

There are just so many possible causes, it's not reasonable to try to guess. The more you do with Auto Layout, the greater the risk of this kind of message.

If you have specific requirement to deploy to 10.13, that's fine. But macOS 13 is really the oldest reasonable deployment target for most apps.

When the error is printed (at launch time) the method simply called super. Moreover, if I disable the specialized methods, the error is still there.

But the interesting point is that the breakpoint then gives me this backtrace:

0 _NSDetectedLayoutRecursion
53 NSApplicationMain
54 main

It seems to indicate that the error is not related to my codes, at least directly. I suppose it could also be related to some conflicted constraints in my XIB files but, in that case, don't you think the error should have been detected by Interface Builder? Moreover, how could it be related to a layout recursion otherwise than from my codes?

On the other hand, I tested the app and it works without problem on macOS 26, so I don't think I will waste more time on this issue for now.

But anyway, thanks for your help!

Same here on launch. All AppKit methods/functions on the call stack. Not sure what I could do to resolve.

Still running into this regularly. I think it may have something to do with using NSStatusBarButton. Is your app using NSStatusBarButton by any chance?

Might be related to the timing of when I set the button's image. Really AutoLay is being overly aggressive IMO.

I have another window in my app (besides what's created with from status bar button.

And in the initializer are started returning nil (so only the status bar button would be created). And I still hit _NSDetectedLayoutRecursion. I don't think I have any other window created at this time but maybe I do.. I'll have to look deeper... think it has something to do with that button, though.

No, I don't use NSStatusBarButton in my codes, sorry.

More generally, since I run Xcode on macOS 15, the number of such messages has become incredibly high... many of those logs have clearly nothing to do with what is under my control (the worse being when using UBSan and friends).

This ultimately lead me to the deactivation of all logs (os_activity_mode=disable), but after that I couldn't even see some of my own logs anymore...

I finally started using printf as a work around, but if someone knows a good solution to disable all system logs while keeping my own, I'm interested, thanks!

Accepted Answer

I don’t have any good input on the original issue that kicked off this thread, but I wanted to address this:

if someone knows a good solution to disable all system logs while keeping my own

Xcode 15 introduced a new Console area with excellent integration with the system log. For example, consider this program:

import Foundation
import Network
import os.log

let log = Logger(subsystem: "", category: "")

func main() {
    let c = NWConnection(host: "example.com", port: 80, using: .tls)
    c.stateUpdateHandler = { newState in
        log.log("did change state, state: \( "\(newState)" )")
    }
    c.start(queue: .main)
    log.log("will enter main loop")
    dispatchMain()
}

main()

IMPORTANT This is not meant to work. It tries to connect to the HTTP port using TLS, which won’t end well. The goal is to generate a bunch of log entries from Network framework, which it absolutely does (-:

When I run this (Xcode 26.4 on macOS 26.3.1) I see this:

will enter main loop
did change state, state: preparing
quic_protector_key_update unsupported TLS ciphersuite: 0
… lots of stuff …
nw_endpoint_flow_failed_with_error [C1.1.1 mask.icloud.com:443 cancelled intermediate-flow ((null))] already failing, returning
did change state, state: waiting(-9836: bad protocol version)

It’s hard to distinguish my log entries from all the stuff generated by Network framework. To focus on just my stuff, I pasted library:Test806471 into the filter box at the bottom of the Console area (where Test806471 is the name of my tool). After that I see just this:

will enter main loop
did change state, state: preparing
did change state, state: waiting(-9836: bad protocol version)

Nice!


For lots more hints and tips on how to use the system log effectively, see Your Friend the System Log.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi Quinn, thanks for the reply, that's very interesting.

Practically, I have to type the name of each library generating my logs: typing the Process name doesn't seem to log anything (although the system proposes it to me). On the other side, if I type the PID, then I get all system logs again.

Even for the app's main module, I have to enter a Library name of this form: myApp.debug.dylib. But apart of that, it works very well: I can event retrieve my printf statements by adding stdio in the filter.

The only issue is that such a composite filter is a little bit tricky to enter. I thought that it was possible to reuse it through the "Recents" section of the filter menu. But, after giving a try, it appears that this section is cleared on each Xcode launch... that's too bad. It would be very useful to have a way to store frequent filters some way, or maybe to active one from the Scheme (e.g. as it can be done for the OS_ACTIVITY_MODE).

Anyway, your post was very helpful, thanks again!

for the app's main module, I have to enter a Library name of this form: myApp.debug.dylib.

That’s not my experience. When I put this code into an app, the library for each log point is Test806471.debug.dylib but pasting Library:Test806471 into the field still works. If you click on the arrow for that token you can see why. It presents a popup menu with various options, and one of those options lets you select between Equals and Contains, with Contains being the default.

it appears that this section is cleared on each Xcode launch

Indeed. I’ve seen complaints about that before, and some spelunking turned by the relevant bug (r. 130497236). If you want to be notified if and when that gets fixed, file your own bug and ask that it be marked as a dup of that one. See Bug Reporting: How and Why? for lots more hints and tips about filing bugs.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Layout recursion error message
 
 
Q