Entries are organised by the instrument that missed the failure, not by the technology involved. Each states the false reading, the true state, why the instrument cannot separate them, and one discriminating check. A check qualifies only if it returns different output under the two hypotheses.
Every entry is a failure that genuinely occurs. Entries marked provenance 'observed' were diagnosed first-hand during the work that produced this site. Entries marked 'documented' cite primary documentation and their discriminating check was reproduced before publication. None are hypothetical.
The compositor-free browser reports frozen animation as no animation
reads as
A screenshot shows the element static. Conclusion drawn: the animation is badly designed, or the values are wrong.
actually
requestAnimationFrame never fires because the headless browser has no compositor. Every rAF-driven counter, canvas loop and scroll handler is frozen at frame zero, whatever its quality.
blind because
A still image cannot distinguish 'renders one frame then stops' from 'renders one frame correctly'. Both produce the same pixels.
the check
let n=0; requestAnimationFrame(()=>n++); setTimeout(()=>console.log('rAF fired:', n), 1000)
cost of missing
Every visual parameter gets tuned against a frame the animation never advances past. The tuning is not merely useless, it is fitted to an artifact.
generalises to
Any observation instrument that shares a failure mode with the thing observed.
A var assignment silently overwrites a hoisted function of the same name
reads as
A canvas is blank and unanimated. Conclusion drawn: a rendering or design problem.
actually
The file already used `var start` as a timestamp. A later `function start()` was hoisted, then overwritten by the number at execution. The call that scheduled initialisation threw a TypeError and init never ran. The element sat at its untouched default size with zero painted pixels.
blind because
An element that renders nothing and an element that renders badly both look like a design problem in a screenshot. Nothing distinguishes them visually.
the check
Read the element's backing store, not its appearance: canvas.width/height still at the 300x150 default means resize() never ran.
cost of missing
Four rounds of visual tuning applied to a layer that was never drawing.
generalises to
Any name reused across a value and a declaration in the same scope.
A relative font URL resolves one directory too deep and fails into a plausible fallback
reads as
Text renders in a serif. Conclusion drawn: the font loaded.
actually
A stylesheet at /assets/fonts.css requesting url('assets/fonts/x.woff2') resolves to /assets/assets/fonts/x.woff2 and 404s. The browser substitutes a system serif without complaint.
blind because
The fallback is a working font. Only someone who knows the intended typeface can see the substitution, and only by comparison.
the check
document.fonts.check('1em "Family Name"') or a 404 on the font path in the network log.
cost of missing
Design review proceeds against the wrong typeface. Every judgement about weight, rhythm and scale is made on a substitute.
generalises to
Every fallback that is good enough to pass inspection: default configs, cached credentials, stub implementations.
Scraping the largest asset returns a recommendation, not the subject
reads as
A parser extracts an image from the page and it is a valid, plausible image. Conclusion drawn: extraction succeeded.
actually
The page embeds related items alongside the subject. Ranking candidates by size or document order can return a neighbour, which is equally valid and equally wrong.
blind because
Both results are real images from the correct domain. Nothing about the artifact reveals it is the wrong one.
the check
Prefer the canonical marker the page declares about itself (og:image, canonical link, structured data) over any heuristic ranking of candidates.
cost of missing
Silent substitution. Detected only when two different inputs return the same output.
generalises to
Any extraction from a document that also describes things other than itself.
set -e aborts a script at a validation step that concerns something else
reads as
The install script ran and the config file is in place. Conclusion drawn: the change is active.
actually
A validation step covering the whole configuration failed on an unrelated block that needed an environment variable the script did not load. Under set -e the script exited before the reload.
blind because
The steps before the failure completed and left visible artifacts. Partial success looks like success when only the artifacts are inspected.
the check
Ask the running service what it loaded, not the filesystem what it holds. For Caddy: the admin API's live config.
cost of missing
The config is correct on disk and absent from the process, an inconsistency that survives inspection of either side alone.
generalises to
Any pipeline where a global check gates a local change.
Assets are not slow, they are queued behind synchronous work
reads as
Images take seconds to appear. Conclusion drawn: the images are too large.
actually
Their request had not been issued. Heavy synchronous work earlier in the document held the main thread, and the fetch that would load them sat unsent behind it.
blind because
Slow arrival and late departure are the same experience from the viewport.
the check
performance.getEntriesByType('resource') — startTime separates 'requested late' from 'transferred slowly'.
cost of missing
Assets get compressed, resized and lazy-loaded, which lowers quality without touching the delay.
generalises to
Any queue where wait time is read as service time.
Reveal-on-scroll renders a blank page when the observer never fires
reads as
A page loads blank in an embedded or scripted context. Conclusion drawn: a rendering failure.
actually
Elements start at opacity 0 and are revealed by an IntersectionObserver callback. Where the observer does not fire, the page is fully present and fully invisible.
blind because
The DOM is complete and correct. Only computed opacity distinguishes it from a page that failed to build.
the check
Compare element count against visible count: document.querySelectorAll('.reveal').length versus those with computed opacity above zero.
cost of missing
A working page is diagnosed as broken. Worse, the reverse: a genuinely blank page is dismissed as this.
mitigation
Any progressive-enhancement pattern that hides content by default needs a timeout that shows it regardless.
generalises to
Every design where the default state is invisible and visibility depends on a callback.
The OOM killer names the fattest process, not the one that leaked
reads as
A long-running session dies mid-task. The log records that session being killed. Conclusion drawn: that session was the problem.
actually
A different process had leaked for hours — 193 browser instances spawned by automation and never closed, 27 still resident. The OOM killer selects by current footprint, so it shot the largest process, which was an unrelated session whose context had simply grown. The leak and the casualty were different processes.
blind because
The log faithfully records the victim. It has no field for the cause, and nothing in the kill message distinguishes 'grew large' from 'made the machine run out'.
the check
Rank every process by RSS at the time of death, not just the one named: ps -eo rss,comm --sort=-rss | head -20, and count instances of anything spawned in a loop. A single fat process is a victim; a hundred medium ones are the cause.
cost of missing
The innocent session is blamed and 'fixed'. The leak keeps running and takes another process later.
mitigation
Cap or close anything spawned per-iteration, and check free memory before adding load rather than after losing work.
generalises to
Every resource-exhaustion system that reports which tenant it evicted rather than which one filled the resource.
A teardown script destroys the environment it is executing inside
reads as
Several long-running sessions vanish at once with no error output. Conclusion drawn: the tool crashed, or the machine failed.
actually
A rebuild script ran `kill-session` against the multiplexer session it was itself running in. It killed its own parent, taking four unrelated sessions with it. There is no crash and no error because the script did exactly what it was told.
blind because
A process that is killed cannot report that it was killed, and cannot report why. The absence of an error reads as an unexplained crash rather than a successful destructive command.
the check
Before any teardown, compare the target against the environment you occupy: for tmux, test whether $TMUX is set and whether its session name equals the target. Refuse if they match.
cost of missing
Work in progress across every session in the environment, lost with no diagnostic trail.
generalises to
Any tool that can destroy a container, session, service, or host that it might itself be running inside.
A privilege prompt with nowhere to appear hangs instead of failing
reads as
A deploy step produces no output and does not return. Conclusion drawn: the operation is slow, or the network is stalling.
actually
The command needed a password. There is no terminal to prompt on, so it waits indefinitely. No error, no exit code, no timeout.
blind because
Exit codes only exist for processes that exit. An instrument that reads return status has nothing at all to read, and silence resembles work in progress.
the check
Ask whether credentials are needed before running the real command: sudo -n true returns non-zero immediately when a password would be required.
cost of missing
An agent waits on a command that will never return, and a task that needed a human is reported as in progress.
mitigation
Wrap anything that might prompt in a timeout, so a hang converts into a failure you can observe.
generalises to
Every interactive prompt reached from a non-interactive context: credentials, confirmations, pagers, editors.
A pipeline returns the status of its last command, not its failing one
reads as
`npm test | tee build.log` exits zero and the log file is written. Conclusion drawn: the tests passed.
actually
A shell reports the exit status of the last command in a pipeline. The test runner exited 1; tee wrote the log and exited 0, and 0 is what the pipeline returns. `set -e` does not intervene, because the pipeline as a whole succeeded.
blind because
One number is produced for a chain of processes. The failing member's status is overwritten by its successor's, and the overwrite leaves no trace in the value the caller reads.
the check
Read the whole vector rather than the summary: `false | true; echo "${PIPESTATUS[@]}"` prints `1 0` where `$?` prints `0`. Or set `pipefail` first: `set -o pipefail; false | true` exits 1 where the same pipeline without it exits 0.
cost of missing
Every failure inside a command piped into tee, grep, jq, head or a formatter is recorded as success. A build stays green across a broken test run, and the log written alongside it is treated as proof.
mitigation
`set -euo pipefail` at the top of any script whose exit status will be believed by something else.
generalises to
Any composition that collapses several results into one and keeps the last rather than the worst.
curl exits zero after successfully downloading an error page
reads as
`curl -s -o data.json URL` exits 0 and data.json exists with content in it. Conclusion drawn: the fetch succeeded.
actually
The server answered 404 or 500. curl's task — transferring what the server chose to send — completed without fault, so the exit status is 0 and an HTML error page is now sitting in data.json under the name of the expected document.
blind because
The exit code describes the transfer, not the response. A transferred error page is a completed transfer, indistinguishable at that layer from a transferred payload.
the check
Ask for the status separately, or make curl care about it: `curl -s -o data.json -w '%{http_code}\n' URL`, or add `--fail`, which converts HTTP >= 400 into exit code 22. Observed on a 404: plain curl exits 0, `--fail` exits 22.
cost of missing
A downstream step parses an HTML error page as the config, dataset or credential file it expected. The failure surfaces at the parser, far from the request that caused it.
generalises to
Every client whose success criterion is that the protocol completed, rather than that the answer was the one asked for.
A test that does not match the discovery pattern is neither run nor reported
reads as
pytest exits 0 with a green summary after a new test is added. Conclusion drawn: the new test passes.
actually
Collection matches `test_*.py` or `*_test.py` files, and `test`-prefixed functions or methods inside `Test`-prefixed classes. A file named `tests_auth.py`, or a function named `check_expiry`, is never collected. The green result belongs entirely to the other tests. The exit code that signals an empty run, 5, applies only when nothing at all was collected, so any other test in the suite conceals the omission.
blind because
An uncollected test produces no pass line and no fail line. The summary counts what ran; it has no term for what was skipped by never being seen.
the check
`pytest --collect-only -q | grep expiry` — prints the node id if the test was collected, prints nothing if it was not. The same command distinguishes the two cases before any test is executed.
cost of missing
The behaviour the test was written to protect is unprotected, and the suite's green status is subsequently cited as evidence that it is protected.
generalises to
Any convention-driven runner where registration is implicit and non-registration is silent: test discovery, plugin loaders, autoloaded fixtures, route decorators.
A bare mock answers to method names the real object no longer has
reads as
The suite is green after a collaborator's method is renamed. Conclusion drawn: nothing depended on the old name.
actually
`Mock()` manufactures an attribute on first access and returns another Mock, which is callable and truthy. Code calling `client.charge_card(...)` against the double passes although the real class now exposes only `charge`. The test exercises an interface that no longer exists, and will keep passing however far the real object drifts.
blind because
The assertion is satisfied by the double's auto-created child. Green is a true statement about the mock, and the exit code cannot say which object the statement was about.
the check
Derive the double from the real class: `create_autospec(Client)` or `Mock(spec=Client)` raises AttributeError on exactly the call a bare `Mock()` accepted. Observed on 3.12: `Mock().exsits()` returns a truthy Mock; `create_autospec(Real).exsits()` raises AttributeError.
cost of missing
A rename is shipped with a fully green suite whose coverage of the renamed path is zero. The regression appears in production, in code the tests appeared to cover.
generalises to
Every test double whose surface is invented rather than derived from the thing it replaces.
Outside strict mode MySQL stores an adjusted value and calls the statement successful
reads as
The INSERT returns `Query OK, 1 row affected` and the client exits 0. Conclusion drawn: the row was stored as supplied.
actually
With strict mode absent from sql_mode, MySQL 'inserts adjusted values for invalid or missing values and produces warnings'. A string longer than the column is truncated to fit; `'abc'` into an integer column becomes 0. The statement is not aborted and the affected-row count is the same as for a clean insert.
blind because
Warnings are a separate channel that must be asked for. Neither the return status nor the row count changes when a value is adjusted, so the two outcomes are identical to anything reading the result of the statement.
the check
`SHOW WARNINGS` (or `SHOW COUNT(*) WARNINGS`) immediately after the statement, in the same session: it returns rows such as `Data truncated for column ...` only when a value was adjusted, and nothing when it was not.
cost of missing
Truncated identifiers and coerced numbers are indistinguishable from real data once written, and the originals are gone. Corruption is discovered by a later join that finds nothing.
mitigation
Assert the mode rather than assume it: `SELECT @@SESSION.sql_mode` should contain STRICT_TRANS_TABLES before any load is trusted.
generalises to
Any writer that repairs input rather than rejecting it: lenient parsers, schema-on-read stores, spreadsheet imports.
S3 sends 200 OK before it knows whether the upload completed
reads as
CompleteMultipartUpload returns HTTP 200. Conclusion drawn: the object is assembled and present.
actually
S3 sends the 200 header first, then keeps the connection alive with whitespace while assembly runs, which can take minutes. A failure after that point is delivered as an `<Error>` document in the body of the response whose status line already said 200. The API reference states it directly: a 200 OK response can contain either a success or an error.
blind because
The status line is written before the outcome is known, so it cannot encode the outcome. A client that reads the status and closes has read a value committed in advance of the fact it is taken to report.
the check
Parse the body even on 200 and look for an `<Error>` root element; or confirm independently with HeadObject and compare ContentLength and ETag against what was uploaded. Both differ between a completed and a failed assembly; the status code does not.
cost of missing
An upload pipeline records success for an object that does not exist. The gap is found by whatever reads it next, typically much later and in another system.
generalises to
Any protocol that must acknowledge before it can know: streamed responses, long-polling, 202-style accepted work, write-behind caches.
A batch write returns 200 while handing back the items it did not write
reads as
BatchWriteItem returns HTTP 200 and the SDK raises no exception. Conclusion drawn: all 25 items were written.
actually
The individual puts and deletes are atomic but the batch is not. Operations that failed on throughput or an internal error are returned in `UnprocessedItems` inside the 200 body, and the caller is expected to resubmit them with backoff. The low-level client hands them back; only higher-level helpers, such as boto3's batch_writer, resubmit on their own.
blind because
Total success and partial success share a status code, an exception-free return and a well-formed body. The difference is one map that is empty in the first case and populated in the second.
the check
Assert the map is empty rather than assuming it: `sum(len(v) for v in resp.get('UnprocessedItems', {}).values()) == 0`. It is 0 on a full write and non-zero whenever items were dropped.
cost of missing
Rows go missing from a bulk load in proportion to how throttled the table was, with no error recorded anywhere, and the load is reported complete.
generalises to
Every bulk endpoint that reports transport success while carrying per-item failure in its payload.
A single-page app's catch-all rewrite answers 200 for URLs that do not exist
reads as
`curl -o /dev/null -w '%{http_code}' https://site/docs/pricing` returns 200. Conclusion drawn: the page exists and the link is good.
actually
The host rewrites every unmatched path to index.html so the client-side router can handle it. The bytes returned are the application shell; the router decides only in the browser that there is nothing at this route. Google names the pattern a soft 404 and notes that such apps report 200 instead of the appropriate status code.
blind because
The status is produced by the server before any router exists. Every path under the domain, real or invented, returns the same 200 with the same shell and the same content type.
the check
Compare against a path that certainly does not exist: `curl -s $BASE/zzz-not-a-real-path | md5sum` and `curl -s $URL | md5sum`. Identical hashes mean the catch-all answered both; different hashes mean the URL has its own document.
cost of missing
Link checks, sitemap validation and 'the page is live' claims all pass against URLs with nothing behind them.
generalises to
Any fallback that answers on behalf of everything unmatched: wildcard DNS, default vhosts, permissive proxy routes.
sshd takes the first value for a keyword, so an appended directive loses to an include
reads as
/etc/ssh/sshd_config ends with `PasswordAuthentication no`, and sshd reloaded without error. Conclusion drawn: password logins are disabled.
actually
The man page states that unless noted otherwise, for each keyword the first obtained value will be used. On a stock Ubuntu image `Include /etc/ssh/sshd_config.d/*.conf` sits at line 12 of a 131-line file, so a drop-in such as 50-cloud-init.conf that sets the same keyword is read first and wins. The line appended at the bottom is parsed and discarded.
blind because
The file says what was intended, and it is the file that was edited. Precedence is a property of the merge order across several files, and the include that pre-empts the edit sits above it, out of the region being read.
the check
`sudo sshd -T | grep -i passwordauthentication` prints the effective merged value the daemon will use, which differs from the authored line whenever an earlier occurrence won. Run it with root privileges: as an unprivileged user it silently omits unreadable drop-ins.
cost of missing
A hardening change is recorded as applied while the setting it was meant to change is untouched, and the evidence for the claim is the file that lost.
generalises to
Every first-wins configuration system, which fails in exactly the opposite direction to the last-wins ones and therefore defeats the habit built on them.
Bidirectional control characters make source read differently than it compiles
reads as
A reviewer reads the diff and the early return is plainly inside a comment. Conclusion drawn: the change is inert.
actually
Unicode bidirectional overrides (U+202A to U+202E, U+2066 to U+2069) reorder the display of tokens without changing their logical order. Compilers and interpreters adhere to the logical ordering of source code, not the visual order, so the code executed is not the code rendered. Catalogued as CVE-2021-42574, with a homoglyph variant as CVE-2021-42694.
blind because
Reading a file means reading a rendering of it. The terminal, the editor and the diff viewer all apply the same bidi algorithm as the attack, so the instrument and the exploit agree with each other and disagree with the compiler.
the check
Search for the characters instead of reading the text: `grep -rlP '[\x{202A}-\x{202E}\x{2066}-\x{2069}]' path/` names files containing them and prints nothing for files that do not. Verified against a planted sample and a clean file.
cost of missing
Code is reviewed and approved on the strength of behaviour no reviewer ever saw.
mitigation
Compilers now detect this where asked: rustc's text_direction_codepoint_in_literal lint and gcc's -Wbidi-chars. Enable them rather than relying on reading.
generalises to
Any check performed on a rendering of an artifact rather than on its bytes.
A JSON integer above 2^53 is silently rounded when parsed as a double
reads as
The response contains `"id": 10765432100123456789`; the parsed object has an id of the right shape and it round-trips through the code. Conclusion drawn: the identifier was carried through intact.
actually
JavaScript parses JSON numbers as IEEE 754 doubles. The value becomes 10765432100123458000 — a different, equally plausible, non-existent identifier. RFC 8259 states that only integers within [-(2**53)+1, (2**53)-1] are interoperable in the sense that implementations will agree exactly on their values.
blind because
The corrupted value has the same type, similar magnitude and identical formatting. The sender's logs show the original and the receiver's show the rounded one, so each side is internally consistent and only a comparison across the boundary reveals the change.
the check
`Number.isSafeInteger(value)` — false for anything already rounded, true otherwise — or compare re-serialisation against the received text: `JSON.stringify(JSON.parse(s)) === s`. Verified: 10765432100123456789 parses to 10765432100123458000, isSafeInteger false, round-trip unequal; the same document parses exactly in Python.
cost of missing
Reads and writes land on the wrong record or on none. The wrongness is stable and reproducible, which makes it look like data rather than corruption.
mitigation
Carry large identifiers as strings across the boundary; APIs that learned this the hard way ship both forms, id and id_str.
generalises to
Every boundary between systems with different numeric ranges: 64-bit ids into doubles, timestamps into 32-bit seconds, decimals into floats.
systemd reports a Type=simple unit active before the service binary has been executed
reads as
`systemctl start app` returns and `systemctl is-active app` says active. Conclusion drawn: the service is up and accepting connections.
actually
For Type=simple the service manager considers the unit started immediately after the main service process has been forked off — after fork() and before the new process has called execve() to invoke the actual service binary. A unit whose binary is missing, whose port is already taken, or which needs thirty seconds to warm up, is 'active' throughout.
blind because
The process list reports existence and state. A process that will fail in a moment exists now, and readiness is simply not a quantity the manager measures for this type.
the check
Ask the socket rather than the manager: `ss -ltnp 'sport = :8000'` returns a listener only when one exists, and is empty while the unit is active but not yet serving. A single request to the port distinguishes the same two states.
cost of missing
Dependent units and deploy scripts proceed against a service that is not listening. The ordering guarantee that was assumed was never offered.
mitigation
Type=notify with sd_notify(READY=1) makes activeness mean readiness; Type=exec at least waits for execve() to succeed.
generalises to
Every start-up API that acknowledges the request rather than the readiness.
A service crash-looping every few seconds reads as active between crashes
reads as
`systemctl status app` shows active (running) with a PID. Conclusion drawn: the service is healthy.
actually
With Restart=always the unit crashes, waits RestartSec, and starts again. Sampled during a run it is active (running) with a fresh PID; sampled during the pause it is activating (auto-restart). Nothing in one sample says the PID is four seconds old and that fifty predecessors are gone.
blind because
The process list is a snapshot. A rapidly replaced process and a stable one are identical in any single frame; only the identity of the PID across frames separates them.
the check
Read the restart counter and the start timestamp twice, thirty seconds apart: `systemctl show -p NRestarts -p ExecMainStartTimestamp --value app`. A stable service returns the same two values both times; a flapping one returns different ones. Both properties are exposed by systemd for every service unit.
cost of missing
A deploy is signed off on a service that drops every request arriving inside its restart window, until the start rate limit is reached and it stays down for good.
generalises to
Every supervised process where the supervisor's diligence in restarting is read as the process's success in running.
A rotated log leaves the daemon writing to a file that no longer has a name
reads as
app.log exists, is zero bytes, and gains no lines. Conclusion drawn: the service is idle, or has stopped working.
actually
logrotate renamed or removed the file the process had open. The process still holds the old inode and keeps appending to it. The logrotate man page names the case in its description of copytruncate: it exists for programs that cannot be told to close their logfile and thus might continue writing to the previous log file forever.
blind because
Reading a log means resolving a path. After rotation the path and the process's open descriptor refer to different objects, and the reader follows the path while the writer holds the descriptor.
the check
Ask the process which file it is writing to: `ls -l /proc/$(pidof app)/fd | grep -i log`. A healthy process points at the live path; a stranded one points at a path marked `(deleted)`.
cost of missing
Log-based monitoring goes quiet and the quiet is read as calm. Disk fills with a file no directory listing can show, and it is only reclaimed when the process is restarted.
mitigation
copytruncate, or a postrotate hook that signals the daemon to reopen its log.
generalises to
Any handle held across a rename or delete: log files, config files watched by path, unlinked sockets and temp files.
Python discards records below WARNING when no logging is configured
reads as
A script instrumented with logger.info() at every step produces no output at all. Conclusion drawn: the code path never ran.
actually
With no configuration, the root logger has no handlers and the internal last-resort handler is set at WARNING. INFO and DEBUG records are created and then dropped; WARNING and above go to stderr. The code ran, and said so, into nothing.
blind because
A discarded record and a record that was never emitted produce the same empty output. A log cannot report what it filtered out, because the filtering happens before anything is written.
the check
`logging.getLogger(__name__).isEnabledFor(logging.INFO)` — False while records are being dropped, True once a handler and level are configured. Observed on 3.12: root handlers `[]`, lastResort `<_StderrHandler <stderr> (WARNING)>`, isEnabledFor(INFO) False.
cost of missing
Debugging proceeds from the false premise that the instrumented branch was not reached, and the real fault is hunted upstream of where it lives.
generalises to
Every level-filtered or sampled telemetry channel, where the absence of a line is read as the absence of an event.
A transparent overlay takes the click the screenshot shows landing on the button
reads as
The screenshot shows the button unobscured and correctly placed, and the click was dispatched without error. Conclusion drawn: the button was clicked.
actually
A transparent element — a full-viewport modal backdrop, a zero-opacity loading layer, an oversized decorative pseudo-element — covers the button's centre point, and hit testing delivers the event to the topmost element at that coordinate. WebDriver has a named error for exactly this: the Element Click command could not be completed because the element receiving the events is obscuring the element that was requested clicked.
blind because
A transparent overlay contributes no pixels. The image of a covered button and the image of an uncovered one are the same image.
the check
Ask the document what occupies the point: `const r = el.getBoundingClientRect(); document.elementFromPoint(r.left + r.width/2, r.top + r.height/2) === el` — true when the element would receive the click, false when something is over it.
cost of missing
An automated flow reports submitting forms it never submitted. A synthetic `el.click()` compounds it, because dispatching on the element directly bypasses hit testing and succeeds where a real user's click would not.
generalises to
Any interaction verified by appearance rather than by the effect the interaction was supposed to have.