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 Slide

  2. INWH.SE/WIN

    View Slide

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

    View Slide

  4. 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 Slide

  5. 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 Slide

  6. 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 Slide

  7. 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 Slide

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

    View Slide

  9. 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 Slide

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

    View Slide

  11. 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 Slide

  12. LOGGING FROM THE
    BROWSER

    View Slide

  13. 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 Slide

  14. 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 Slide

  15. 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 Slide

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

    View Slide

  17. 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 Slide

  18. CATCHING ERRORS

    View Slide

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

    View Slide

  20. 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 Slide

  21. 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 Slide

  22. 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 Slide

  23. MEANWHILE AT W3C...

    View Slide

  24. 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 Slide

  25. 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 Slide

  26. 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 Slide

  27. 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 Slide

  28. 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 Slide

  29. 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 Slide

  30. THROWING ERRORS

    View Slide

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

    View Slide

  32. 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 Slide

  33. 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 Slide

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

    View Slide

  35. 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 Slide

  36. 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 Slide

  37. 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 Slide

  38. 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 Slide

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

    View Slide