False positive on writeToURL:atomically:encoding:error: ?

  • Dear list,

    I have an interesting bug report from a user of an app of mine. The app manages files and allows the user to edit them. When they save the project each file is saved to disk (if necessary). They are experiencing what appears to be a false positive of writeToURL:atomically:encoding:error:. The file actually does save, but the error comes back non-nil and when presented says:

    "You don’t have permission to save the file “XXX” in the folder “YYY”.

    The piece of code I use is

      NSError *error = nil;
      [content writeToURL:aURL atomically:YES encoding:encoding error:&error];
      if (error) {
        [NSApp presentError:error];
        return NO;
      }

    By giving the user a debug version of the app with lots of NSLog statements, we narrowed it down to the above code. So even though the file is saved, 'error' comes back non-nil.

    Has anyone seen such behaviour before, or does anyone have any idea how to further investigate this?

    Best wishes,

    Martin
  • In general, it is not safe to assume that errors from Cocoa frameworks are cleared when an operation succeeds (in fact, I believe that they they are almost never cleared). The only way to determine if "writeToURL" succeeds is to test the return value (not the error). If the return value is YES, the error is garbage. If the return value is NO, the error will have meaning.

    NSError *error = nil;
    BOOL success = [content writeToURL:aURL atomically:YES encoding:encoding error:&error];
    if (!success)
    {
    [NSApp presentError:error];
    return NO;
    }

    Aaron

    On Jun 23, 2012, at 10:50 AM, Martin Hewitson wrote:

    > Dear list,
    >
    > I have an interesting bug report from a user of an app of mine. The app manages files and allows the user to edit them. When they save the project each file is saved to disk (if necessary). They are experiencing what appears to be a false positive of writeToURL:atomically:encoding:error:. The file actually does save, but the error comes back non-nil and when presented says:
    >
    > "You don’t have permission to save the file “XXX” in the folder “YYY”.
    >
    > The piece of code I use is
    >
    > NSError *error = nil;
    > [content writeToURL:aURL atomically:YES encoding:encoding error:&error];
    > if (error) {
    > [NSApp presentError:error];
    > return NO;
    > }
    >
    > By giving the user a debug version of the app with lots of NSLog statements, we narrowed it down to the above code. So even though the file is saved, 'error' comes back non-nil.
    >
    > Has anyone seen such behaviour before, or does anyone have any idea how to further investigate this?
    >
    > Best wishes,
    >
    > Martin
  • Martin,

    Instead of inspecting the value of error, you should be inspecting the return value of writeToURL:atomically:encoding:error:. Only if that returns NO should you be inspecting the value of error which, as you’ve seen, may be non-nil on success. You can see an example here:

    https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Stri
    ngs/Articles/readingFiles.html#//apple_ref/doc/uid/TP40003459-SW5


    Jeff Kelley

    On Jun 23, 2012, at 1:50 PM, Martin Hewitson wrote:

    > Dear list,
    >
    > I have an interesting bug report from a user of an app of mine. The app manages files and allows the user to edit them. When they save the project each file is saved to disk (if necessary). They are experiencing what appears to be a false positive of writeToURL:atomically:encoding:error:. The file actually does save, but the error comes back non-nil and when presented says:
    >
    > "You don’t have permission to save the file “XXX” in the folder “YYY”.
    >
    > The piece of code I use is
    >
    > NSError *error = nil;
    > [content writeToURL:aURL atomically:YES encoding:encoding error:&error];
    > if (error) {
    > [NSApp presentError:error];
    > return NO;
    > }
    >
    > By giving the user a debug version of the app with lots of NSLog statements, we narrowed it down to the above code. So even though the file is saved, 'error' comes back non-nil.
    >
    > Has anyone seen such behaviour before, or does anyone have any idea how to further investigate this?
    >
    > Best wishes,
    >
    > Martin
  • Thank you all for these wise words. I'll make the appropriate changes.

    Cheers,

    Martin

    On 23, Jun, 2012, at 08:01 PM, Jeff Kelley wrote:

    > Martin,
    >
    > Instead of inspecting the value of error, you should be inspecting the return value of writeToURL:atomically:encoding:error:. Only if that returns NO should you be inspecting the value of error which, as you’ve seen, may be non-nil on success. You can see an example here:
    >
    > https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Stri
    ngs/Articles/readingFiles.html#//apple_ref/doc/uid/TP40003459-SW5

    >
    > Jeff Kelley
    >
    > On Jun 23, 2012, at 1:50 PM, Martin Hewitson wrote:
    >
    >> Dear list,
    >>
    >> I have an interesting bug report from a user of an app of mine. The app manages files and allows the user to edit them. When they save the project each file is saved to disk (if necessary). They are experiencing what appears to be a false positive of writeToURL:atomically:encoding:error:. The file actually does save, but the error comes back non-nil and when presented says:
    >>
    >> "You don’t have permission to save the file “XXX” in the folder “YYY”.
    >>
    >> The piece of code I use is
    >>
    >> NSError *error = nil;
    >> [content writeToURL:aURL atomically:YES encoding:encoding error:&error];
    >> if (error) {
    >> [NSApp presentError:error];
    >> return NO;
    >> }
    >>
    >> By giving the user a debug version of the app with lots of NSLog statements, we narrowed it down to the above code. So even though the file is saved, 'error' comes back non-nil.
    >>
    >> Has anyone seen such behaviour before, or does anyone have any idea how to further investigate this?
    >>
    >> Best wishes,
    >>
    >> Martin
    >

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Martin Hewitson
    Albert-Einstein-Institut
    Max-Planck-Institut fuer
        Gravitationsphysik und Universitaet Hannover
    Callinstr. 38, 30167 Hannover, Germany
    Tel: +49-511-762-17121, Fax: +49-511-762-5861
    E-Mail: <martin.hewitson...>
    WWW: http://www.aei.mpg.de/~hewitson
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  • Just a quick follow up on this.

    It's been stated that it's unsafe to check the error returned from -writeToURL:atomically:encoding:error: and rather one should check the return value. Is this a general statement for other methods which fill an error object on error? For example, what about NSFileManager's -attributesOfItemAtPath:error: ? The documentation makes no statement about the return value in the case of error. Is it standard that the return value will be nil? Or should I check the error object in this case?

    This thread triggered me to check through the whole project for places where I check for non-nil error objects. There are a number, so I'd like to ensure I'm doing the right thing in each case.

    Any insight gratefully received.

    Martin

    On 23, Jun, 2012, at 07:59 PM, Eeyore wrote:

    > In general, it is not safe to assume that errors from Cocoa frameworks are cleared when an operation succeeds (in fact, I believe that they they are almost never cleared). The only way to determine if "writeToURL" succeeds is to test the return value (not the error). If the return value is YES, the error is garbage. If the return value is NO, the error will have meaning.
    >
    > NSError *error = nil;
    > BOOL success = [content writeToURL:aURL atomically:YES encoding:encoding error:&error];
    > if (!success)
    > {
    > [NSApp presentError:error];
    > return NO;
    > }
    >
    > Aaron
    >
    > On Jun 23, 2012, at 10:50 AM, Martin Hewitson wrote:
    >
    >> Dear list,
    >>
    >> I have an interesting bug report from a user of an app of mine. The app manages files and allows the user to edit them. When they save the project each file is saved to disk (if necessary). They are experiencing what appears to be a false positive of writeToURL:atomically:encoding:error:. The file actually does save, but the error comes back non-nil and when presented says:
    >>
    >> "You don’t have permission to save the file “XXX” in the folder “YYY”.
    >>
    >> The piece of code I use is
    >>
    >> NSError *error = nil;
    >> [content writeToURL:aURL atomically:YES encoding:encoding error:&error];
    >> if (error) {
    >> [NSApp presentError:error];
    >> return NO;
    >> }
    >>
    >> By giving the user a debug version of the app with lots of NSLog statements, we narrowed it down to the above code. So even though the file is saved, 'error' comes back non-nil.
    >>
    >> Has anyone seen such behaviour before, or does anyone have any idea how to further investigate this?
    >>
    >> Best wishes,
    >>
    >> Martin
    >

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Martin Hewitson
    Albert-Einstein-Institut
    Max-Planck-Institut fuer
        Gravitationsphysik und Universitaet Hannover
    Callinstr. 38, 30167 Hannover, Germany
    Tel: +49-511-762-17121, Fax: +49-511-762-5861
    E-Mail: <martin.hewitson...>
    WWW: http://www.aei.mpg.de/~hewitson
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  • On Jun 24, 2012, at 1:35 PM, Martin Hewitson wrote:

    > Just a quick follow up on this.
    >
    > It's been stated that it's unsafe to check the error returned from -writeToURL:atomically:encoding:error: and rather one should check the return value. Is this a general statement for other methods which fill an error object on error? For example, what about NSFileManager's -attributesOfItemAtPath:error: ? The documentation makes no statement about the return value in the case of error. Is it standard that the return value will be nil? Or should I check the error object in this case?
    >
    > This thread triggered me to check through the whole project for places where I check for non-nil error objects. There are a number, so I'd like to ensure I'm doing the right thing in each case.
    >
    > Any insight gratefully received.
    >
    > Martin

    Yes that is a general statement. You should check the return value of the method and only if it indicates there's an error, check the error object. There is no guarantee that the error object was not set at some point during the method processing, even if that method eventually succeeds, nor that it's forced clear on success.

    Most such methods are nice enough to return BOOL so it's quite easy, attributesOfItemAtPath:error .. indeed it is not documented what it does in the case of error. I would probably only check the error object if that method returned nil and I would test to see if it does return nil if I give it a bad path or a directory I can't read or something to increase my comfort level. Would be nicer if that were documented of course.
  • As Roland indicated, yes, this is a general statement.

    It would be advisable to read the Error Handling Programming Guide, which includes, among other things:

    "Important Success or failure is indicated by the return value of the method. Although Cocoa methods that indirectly return error objects in the Cocoa error domain are guaranteed to return such objects if the method indicates failure by directly returning nil or NO, you should always check that the return value is nil or NO before attempting to do anything with the NSError object."

    More at:

    https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Err
    orHandlingCocoa/ErrorHandling/ErrorHandling.html


    -Conrad

    On Jun 23, 2012, at 10:35 PM, Martin Hewitson wrote:

    > Just a quick follow up on this.
    >
    > It's been stated that it's unsafe to check the error returned from -writeToURL:atomically:encoding:error: and rather one should check the return value. Is this a general statement for other methods which fill an error object on error? For example, what about NSFileManager's -attributesOfItemAtPath:error: ? The documentation makes no statement about the return value in the case of error. Is it standard that the return value will be nil? Or should I check the error object in this case?
    >
    > This thread triggered me to check through the whole project for places where I check for non-nil error objects. There are a number, so I'd like to ensure I'm doing the right thing in each case.
    >
    > Any insight gratefully received.
    >
    > Martin
  • Gentlemen, thank you for those succinct responses. I'll go through my code base and make the necessary changes.

    Best wishes,

    Martin

    On 24, Jun, 2012, at 07:55 AM, Conrad Shultz wrote:

    > As Roland indicated, yes, this is a general statement.
    >
    > It would be advisable to read the Error Handling Programming Guide, which includes, among other things:
    >
    > "Important Success or failure is indicated by the return value of the method. Although Cocoa methods that indirectly return error objects in the Cocoa error domain are guaranteed to return such objects if the method indicates failure by directly returning nil or NO, you should always check that the return value is nil or NO before attempting to do anything with the NSError object."
    >
    > More at:
    >
    > https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Err
    orHandlingCocoa/ErrorHandling/ErrorHandling.html

    >
    > -Conrad
    >
    > On Jun 23, 2012, at 10:35 PM, Martin Hewitson wrote:
    >
    >> Just a quick follow up on this.
    >>
    >> It's been stated that it's unsafe to check the error returned from -writeToURL:atomically:encoding:error: and rather one should check the return value. Is this a general statement for other methods which fill an error object on error? For example, what about NSFileManager's -attributesOfItemAtPath:error: ? The documentation makes no statement about the return value in the case of error. Is it standard that the return value will be nil? Or should I check the error object in this case?
    >>
    >> This thread triggered me to check through the whole project for places where I check for non-nil error objects. There are a number, so I'd like to ensure I'm doing the right thing in each case.
    >>
    >> Any insight gratefully received.
    >>
    >> Martin
    >

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Martin Hewitson
    Albert-Einstein-Institut
    Max-Planck-Institut fuer
        Gravitationsphysik und Universitaet Hannover
    Callinstr. 38, 30167 Hannover, Germany
    Tel: +49-511-762-17121, Fax: +49-511-762-5861
    E-Mail: <martin.hewitson...>
    WWW: http://www.aei.mpg.de/~hewitson
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  • On Jun 23, 2012, at 10:46 PM, Roland King wrote:

    > Yes that is a general statement. You should check the return value of the method and only if it indicates there's an error, check the error object. There is no guarantee that the error object was not set at some point during the method processing, even if that method eventually succeeds, nor that it's forced clear on success.

    What if you explicitly set your NSError variable's value to nil immediately before using it? Wouldn't it be safe to trust in that case?
  • No.

    On 24 Jun, 2012, at 11:14 PM, Dennis <lists...> wrote:

    > On Jun 23, 2012, at 10:46 PM, Roland King wrote:
    >
    >> Yes that is a general statement. You should check the return value of the method and only if it indicates there's an error, check the error object. There is no guarantee that the error object was not set at some point during the method processing, even if that method eventually succeeds, nor that it's forced clear on success.
    >
    > What if you explicitly set your NSError variable's value to nil immediately before using it? Wouldn't it be safe to trust in that case?
  • On 24, Jun, 2012, at 05:14 PM, Dennis wrote:

    > On Jun 23, 2012, at 10:46 PM, Roland King wrote:
    >
    >> Yes that is a general statement. You should check the return value of the method and only if it indicates there's an error, check the error object. There is no guarantee that the error object was not set at some point during the method processing, even if that method eventually succeeds, nor that it's forced clear on success.
    >
    > What if you explicitly set your NSError variable's value to nil immediately before using it? Wouldn't it be safe to trust in that case?

    I was doing that (I always do that), but the problem is still there. Anyway, now I've changed the app to follow the recommended approach and I check the return value. I shipped a test version to the user with the problems, but they still get the original error messages. In other words, -writeToURL:atomically:encoding:error: is actually returning NO. The user repaired the file permissions on his disk and after that the files in question showed up as being locked. I'll quote the user here:

    "The whole culprit was the new lock feature introduced with Lion. I think files are automatically locked after a few days in Lion. I just repaired the disk permissions and found that the files in question had the lock icon on them. Strangely, the lock icon never appeared for the files before and the "Locked" checkmark was not checked in the info pane for the files. But I guess after repairing the permissions the files were marked as locked.

    What is stranger is, even though they were locked, apps like TextEdit were able to save/edit them without warnings. They usually display an alert with an Unlock button on it."

    So, as developers, are we supposed to check for this lock status before trying to save programmatically? I guess I've got some reading to do about this lock stuff.

    Cheers,

    Martin
  • On Jun 24, 2012, at 8:14 AM, Dennis wrote:

    > What if you explicitly set your NSError variable's value to nil immediately before using it? Wouldn't it be safe to trust in that case?

    To expand on Roland's pithy "No":

    * You call method A with an NSError** that points to a nil pointer.
    * A calls B, passing in the same NSError**.
    * B fails, and sets the error to point to an NSError object describing what failed.
    * A looks at the error and recovers from it somehow (maybe it fixes the problem and calls B again, or maybe it calls C instead and C succeeds.)
    * A returns YES because it succeeded.
    * …but your error variable is still pointing to the error returned by B, even though it doesn't matter to you.

    <sarc>But of course this system is so much easier than using catch/throw exceptions. That would be complicated.</sarc>

    —Jens
previous month june 2012 next month
MTWTFSS
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30  
Go to today