Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The sorry state of Javascript error handling

The sorry state of Javascript error handling

Dominykas Blyžė

June 18, 2015
Tweet

More Decks by Dominykas Blyžė

Other Decks in Programming

Transcript

  1. THE SORRY STATE OF
    JAVASCRIPT ERROR
    HANDLING
    By ( ), 2015-06-18
    Dominykas Blyžė @dymonaz

    View full-size slide

  2. > t
    h
    r
    o
    w T
    a
    b
    l
    e
    ノಠ益ಠノ彡┻━┻
    >
    THROW ALL THE THINGS!
    But then what?

    View full-size slide

  3. JAVASCRIPT IS 20. READY FOR PRODUCTION.
    Seriously, browse the web with the developer
    tools console open logging errors and be truly
    amazed that the internet ever worked.
    @shanselman

    View full-size slide

  4. HANDLE ALL THE THINGS?
    92% of catastrophic failures are the result of
    incorrect error handling
    Simple Testing Can Prevent Most Critical Failures by Ding Yuan et al,
    dominykas.net/24

    View full-size slide

  5. PRO-TIP
    Make sure stuff works without JS:
    Use real <
    a h
    r
    e
    f
    >
    with real URLs
    Use real forms with real buttons
    p
    r
    e
    v
    e
    n
    t
    D
    e
    f
    a
    u
    l
    t
    (
    )
    as a last thing

    View full-size slide

  6. ERROR HANDLING IN THE NORMAL WORLD
    1. Stay aware of errors
    2. Provide detailed error information
    3. Add exception handling code
    4. If all else fails - crash with a dump

    View full-size slide

  7. IN THE BROWSER WORLD...
    Old browsers
    3rd party scripts
    Browser extensions
    Cross-domain restrictions

    View full-size slide

  8. IN THE BROWSER WORLD...
    t
    h
    r
    o
    w n
    e
    w E
    r
    r
    o
    r
    vs t
    h
    r
    o
    w o
    l
    d
    _
    T
    a
    b
    l
    e
    Errors in async functions
    Errors in callbacks
    Zalgo

    View full-size slide

  9. IN THE BROWSER WORLD...
    Network errors
    Aggressive caching
    Random obscure

    View full-size slide

  10. RANDOM OBSCURE
    XHR randomly status 0 (but error logged CORS XHR)
    <
    i
    f
    r
    a
    m
    e i
    d
    =
    "
    l
    o
    c
    a
    l
    S
    t
    o
    r
    a
    g
    e
    " /
    >
    l
    o
    c
    a
    l
    S
    t
    o
    r
    a
    g
    e
    max size is 0 in private mode on iOS
    g
    e
    t
    C
    o
    m
    p
    u
    t
    e
    d
    S
    t
    y
    l
    e
    stops being a function...
    Random m
    e
    s
    s
    a
    g
    e
    events from plugins

    View full-size slide

  11. LOGGING FROM THE
    BROWSER

    View full-size slide

  12. DO THIS TOMORROW
    If you're not doing it yet
    w
    i
    n
    d
    o
    w
    .
    o
    n
    e
    r
    r
    o
    r = f
    u
    n
    c
    t
    i
    o
    n (
    e
    r
    r
    , u
    r
    l
    , l
    i
    n
    e
    ) {
    g
    a
    (
    '
    s
    e
    n
    d
    '
    , '
    e
    x
    c
    e
    p
    t
    i
    o
    n
    '
    , {
    '
    e
    x
    D
    e
    s
    c
    r
    i
    p
    t
    i
    o
    n
    '
    : l
    i
    n
    e + " " + e
    r
    r
    }
    )
    ;
    }
    ;

    View full-size slide

  13. DO THIS NEXT WEEK
    Set up a proper logging endpoint
    Version your releases and pass on the version
    Make sure versions match across different bundles
    Don't include "data" inside the "message", i.e.
    l
    o
    g
    (
    m
    e
    s
    s
    a
    g
    e
    S
    t
    r
    i
    n
    g
    , o
    b
    j
    D
    a
    t
    a
    )
    ;

    View full-size slide

  14. PAY ATTENTION
    Anything can be thrown - must be able to serialize correctly.
    v
    a
    r l
    o
    g
    M
    e
    s
    s
    a
    g
    e = e
    r
    r &
    & e
    r
    r
    .
    m
    e
    s
    s
    a
    g
    e ? e
    r
    r
    .
    m
    e
    s
    s
    a
    g
    e : "
    " + e
    r
    r
    ;
    > n
    u
    l
    l
    .
    t
    o
    S
    t
    r
    i
    n
    g
    (
    )
    T
    y
    p
    e
    E
    r
    r
    o
    r
    : C
    a
    n
    n
    o
    t r
    e
    a
    d p
    r
    o
    p
    e
    r
    t
    y '
    t
    o
    S
    t
    r
    i
    n
    g
    ' o
    f n
    u
    l
    l
    > n
    e
    w E
    r
    r
    o
    r
    (
    "
    D
    a
    m
    n i
    t
    "
    )
    .
    t
    o
    S
    t
    r
    i
    n
    g
    (
    )
    '
    E
    r
    r
    o
    r
    : D
    a
    m
    n i
    t
    '
    > n
    e
    w E
    r
    r
    o
    r
    (
    "
    D
    a
    m
    n i
    t
    "
    )
    .
    m
    e
    s
    s
    a
    g
    e
    '
    D
    a
    m
    n i
    t
    '

    View full-size slide

  15. TRY
    At the very least they have useful blogs.
    Rollbar
    Errorception

    View full-size slide

  16. DREAM
    C
    o
    n
    t
    e
    n
    t
    -
    S
    e
    c
    u
    r
    i
    t
    y
    -
    P
    o
    l
    i
    c
    y
    : s
    t
    y
    l
    e
    -
    s
    r
    c c
    d
    n
    .
    e
    x
    a
    m
    p
    l
    e
    .
    c
    o
    m
    ; r
    e
    p
    o
    r
    t
    -
    u
    r
    i /
    _
    /
    c
    s
    p
    -
    r
    e
    p
    o
    r
    t
    s

    View full-size slide

  17. CATCHING ERRORS

    View full-size slide

  18. TRY/CATCH
    Ugly
    Sync
    Deoptimizes *
    * no big deal most of the time, but still

    View full-size slide

  19. GUARD()
    f
    u
    n
    c
    t
    i
    o
    n g
    u
    a
    r
    d (
    f
    n
    ) {
    r
    e
    t
    u
    r
    n f
    u
    n
    c
    t
    i
    o
    n (
    ) {
    t
    r
    y {
    r
    e
    t
    u
    r
    n f
    n
    .
    a
    p
    p
    l
    y
    (
    t
    h
    i
    s
    , a
    r
    g
    u
    m
    e
    n
    t
    s
    )
    ;
    } c
    a
    t
    c
    h (
    e
    r
    r
    ) {
    l
    o
    g
    E
    r
    r
    o
    r
    (
    e
    r
    r
    )
    ;
    t
    h
    r
    o
    w e
    r
    r
    ;
    }
    }
    ;
    }
    Using this for auto-wrapping is probably a bad idea.

    View full-size slide

  20. USE PROMISES
    Wrap promise construction
    Reject with n
    e
    w E
    r
    r
    o
    r
    (
    )
    Don't forget to .
    c
    a
    t
    c
    h
    (
    l
    o
    g
    A
    n
    d
    R
    e
    t
    h
    r
    o
    w
    )
    Stack traces an issue (unless native)
    New: u
    n
    h
    a
    n
    d
    l
    e
    d
    R
    e
    j
    e
    c
    t
    i
    o
    n
    Problem: UI can't have a break in event loop

    View full-size slide

  21. WINDOW.ONERROR
    m
    s
    g
    is different on every browser
    Yay! Uncaught Error: undefined is not an object
    u
    r
    l
    has cross-domain restrictions
    l
    i
    n
    e
    n
    o
    is 1, because you minified
    c
    o
    l
    n
    o
    is fairly recent
    e
    r
    r
    o
    r
    O
    b
    j
    is very recent

    View full-size slide

  22. MEANWHILE AT W3C...

    View full-size slide

  23. WINDOW.ONERROR
    Not the same as a
    d
    d
    E
    v
    e
    n
    t
    L
    i
    s
    t
    e
    n
    e
    r
    (
    '
    e
    r
    r
    o
    r
    '
    )
    TraceKit for stacks, if you can afford it
    Run your code in an iframe, if on a third party site
    r
    e
    t
    u
    r
    n t
    r
    u
    e
    if handled

    View full-size slide

  24. WINDOW.ONERROR - CORS
    1. A
    c
    c
    e
    s
    s
    -
    C
    o
    n
    t
    r
    o
    l
    -
    A
    l
    l
    o
    w
    -
    O
    r
    i
    g
    i
    n
    : *
    2. <
    s
    c
    r
    i
    p
    t c
    r
    o
    s
    s
    o
    r
    i
    g
    i
    n
    =
    "
    a
    n
    o
    n
    y
    m
    o
    u
    s
    "
    >

    View full-size slide

  25. WINDOW.ONERROR - CORS
    The script can inject itself with the attribute set
    s
    e
    t
    A
    t
    t
    r
    i
    b
    u
    t
    e
    (
    "
    c
    r
    o
    s
    s
    o
    r
    i
    g
    i
    n
    "
    )
    , not
    s
    c
    r
    i
    p
    t
    .
    c
    r
    o
    s
    s
    o
    r
    i
    g
    i
    n
    i
    f (
    !
    d
    o
    c
    u
    m
    e
    n
    t
    .
    c
    u
    r
    r
    e
    n
    t
    S
    c
    r
    i
    p
    t
    .
    g
    e
    t
    A
    t
    t
    r
    i
    b
    u
    t
    e
    (
    "
    c
    r
    o
    s
    s
    o
    r
    i
    g
    i
    n
    "
    )
    ) {
    v
    a
    r s
    c
    r
    i
    p
    t = d
    o
    c
    u
    m
    e
    n
    t
    .
    c
    r
    e
    a
    t
    e
    E
    l
    e
    m
    e
    n
    t
    (
    "
    s
    c
    r
    i
    p
    t
    "
    )
    ;
    s
    c
    r
    i
    p
    t
    .
    s
    r
    c = d
    o
    c
    u
    m
    e
    n
    t
    .
    c
    u
    r
    r
    e
    n
    t
    S
    c
    r
    i
    p
    t
    .
    s
    r
    c
    ;
    s
    c
    r
    i
    p
    t
    .
    s
    e
    t
    A
    t
    t
    r
    i
    b
    u
    t
    e
    (
    "
    c
    r
    o
    s
    s
    o
    r
    i
    g
    i
    n
    "
    , "
    a
    n
    o
    n
    y
    m
    o
    u
    s
    "
    )
    ;
    d
    o
    c
    u
    m
    e
    n
    t
    .
    h
    e
    a
    d
    .
    a
    p
    p
    e
    n
    d
    C
    h
    i
    l
    d
    (
    s
    c
    r
    i
    p
    t
    )
    ;
    } e
    l
    s
    e {
    /
    * p
    r
    o
    c
    e
    e
    d *
    /
    }

    View full-size slide

  26. SOURCE MAPS
    b
    r
    o
    w
    s
    e
    r
    i
    f
    y \
    -
    e b
    u
    n
    d
    l
    e
    /
    e
    n
    t
    r
    y
    .
    j
    s \
    -
    t u
    g
    l
    i
    f
    y
    i
    f
    y \
    -
    d | e
    x
    o
    r
    c
    i
    s
    t b
    u
    n
    d
    l
    e
    .
    j
    s
    .
    m
    a
    p > b
    u
    n
    d
    l
    e
    .
    j
    s

    View full-size slide

  27. SOURCE MAPS
    v
    a
    r S
    o
    u
    r
    c
    e
    M
    a
    p
    C
    o
    n
    s
    u
    m
    e
    r = r
    e
    q
    u
    i
    r
    e
    (
    "
    s
    o
    u
    r
    c
    e
    -
    m
    a
    p
    "
    )
    .
    S
    o
    u
    r
    c
    e
    M
    a
    p
    C
    o
    n
    s
    u
    m
    e
    r
    ;
    v
    a
    r m
    a
    p = J
    S
    O
    N
    .
    p
    a
    r
    s
    e
    (
    f
    s
    .
    r
    e
    a
    d
    F
    i
    l
    e
    S
    y
    n
    c
    (
    "
    b
    u
    n
    d
    l
    e
    .
    j
    s
    .
    m
    a
    p
    "
    )
    )
    ;
    v
    a
    r s
    m
    c = n
    e
    w S
    o
    u
    r
    c
    e
    M
    a
    p
    C
    o
    n
    s
    u
    m
    e
    r
    (
    m
    a
    p
    )
    ;
    c
    o
    n
    s
    o
    l
    e
    .
    l
    o
    g
    (
    s
    m
    c
    .
    o
    r
    i
    g
    i
    n
    a
    l
    P
    o
    s
    i
    t
    i
    o
    n
    F
    o
    r
    (
    { l
    i
    n
    e
    : 5
    , c
    o
    l
    u
    m
    n
    : 3
    3 }
    )
    )
    ;
    OUTPUT
    {
    s
    o
    u
    r
    c
    e
    : '
    ~
    /
    b
    u
    n
    d
    l
    e
    /
    s
    t
    u
    f
    f
    .
    j
    s
    '
    ,
    l
    i
    n
    e
    : 2
    ,
    c
    o
    l
    u
    m
    n
    : 7
    ,
    n
    a
    m
    e
    : n
    u
    l
    l
    }

    View full-size slide

  28. EMBULACT
    $
    e
    x
    c
    e
    p
    t
    i
    o
    n
    H
    a
    n
    d
    l
    e
    r
    o
    n
    e
    r
    r
    o
    r
    Replay events

    View full-size slide

  29. THROWING ERRORS

    View full-size slide

  30. THROW ERRORS
    If you don't throw, it won't have a stack in IE

    View full-size slide

  31. THROW ERRORS
    If it's not an error, it won't have a stack
    This applies to n
    o
    d
    e
    b
    a
    c
    k
    (
    e
    r
    r
    , d
    a
    t
    a
    )
    O
    b
    j
    e
    c
    t
    .
    c
    r
    e
    a
    t
    e
    (
    E
    r
    r
    o
    r
    )
    is not an error

    View full-size slide

  32. USE ERROR CODES
    v
    a
    r e
    r
    r
    o
    r = n
    e
    w E
    r
    r
    o
    r
    (
    "
    s
    n
    a
    p
    .
    .
    .
    "
    )
    ;
    e
    r
    r
    o
    r
    .
    c
    o
    d
    e = "
    E
    _
    S
    N
    A
    P
    "
    ;
    t
    h
    r
    o
    w e
    r
    r
    o
    r
    ;

    View full-size slide

  33. THROW CUSTOM ERRORS
    I don't want to talk about it...

    View full-size slide

  34. THROW CUSTOM ERRORS
    M
    y
    C
    u
    s
    t
    o
    m
    E
    r
    r
    o
    r
    should:
    Keep m
    e
    s
    s
    a
    g
    e
    as a first param
    Have a m
    e
    s
    s
    a
    g
    e
    property
    Have a n
    a
    m
    e
    =
    "
    M
    y
    C
    u
    s
    t
    o
    m
    E
    r
    r
    o
    r
    "
    property
    Extend E
    r
    r
    o
    r
    (i.e. support i
    n
    s
    t
    a
    n
    c
    e
    o
    f
    )
    Work with and without n
    e
    w

    View full-size slide

  35. ADD USEFUL INFO
    Good custom errors also should:
    Have a reasonable t
    o
    S
    t
    r
    i
    n
    g
    Be able to wrap around existing errors
    Have a stack trace

    View full-size slide

  36. POOR MAN'S S
    T
    A
    C
    K
    T
    R
    A
    C
    E
    -
    J
    S
    f
    u
    n
    c
    t
    i
    o
    n C
    u
    s
    t
    o
    m
    E
    r
    r
    o
    r
    (
    m
    e
    s
    s
    a
    g
    e
    ) {
    v
    a
    r t
    e
    m
    p = E
    r
    r
    o
    r
    .
    c
    a
    l
    l
    (
    t
    h
    i
    s
    , m
    e
    s
    s
    a
    g
    e
    )
    ;
    t
    e
    m
    p
    .
    n
    a
    m
    e = t
    h
    i
    s
    .
    n
    a
    m
    e = '
    C
    u
    s
    t
    o
    m
    E
    r
    r
    o
    r
    '
    ;
    t
    r
    y {
    t
    h
    r
    o
    w t
    e
    m
    p
    ;
    } c
    a
    t
    c
    h (
    a
    f
    t
    e
    r
    T
    h
    r
    o
    w
    ) {
    t
    h
    i
    s
    .
    s
    t
    a
    c
    k = a
    f
    t
    e
    r
    T
    h
    r
    o
    w
    .
    s
    t
    a
    c
    k
    ;
    }
    t
    h
    i
    s
    .
    m
    e
    s
    s
    a
    g
    e = t
    e
    m
    p
    .
    m
    e
    s
    s
    a
    g
    e
    ;
    }
    C
    u
    s
    t
    o
    m
    E
    r
    r
    o
    r
    .
    p
    r
    o
    t
    o
    t
    y
    p
    e = O
    b
    j
    e
    c
    t
    .
    c
    r
    e
    a
    t
    e
    (
    E
    r
    r
    o
    r
    .
    p
    r
    o
    t
    o
    t
    y
    p
    e
    )
    ;

    View full-size slide

  37. NUMBER OF BORING S
    T
    A
    C
    K
    LINES
    In Blink: name/message + 2
    In Webkit: 1
    In Firefox: 1
    In IE: name/message + 1

    View full-size slide

  38. <
    /
    ノಠ益ಠノ彡┻━┻
    >
    Slides: dominykas.net/25
    We're hiring: inwh.se/win
    @dymonaz
    ;

    View full-size slide