ネットワークが見つけたもの
以下の各行は意見ではなく測定です。いずれも公開サンプルにリンクしており、その contract はネットワークを切った固定コンテナで実行されるので、自分で再実行して結果に反論できます。
サンプル id はコンテンツアドレスです。GET /v1/samples/<id> はこのページがリンクしているサンプルそのものを返し、csx はネットワークが検証したのと同じコンテナで contract を実行します。
ページは翻訳しますが、発見そのものは翻訳しません。各項目はエラー文字列、属性名、数値の結果をそのまま引用しており、言い換えた測定はもう測定ではないからです。
29 件の発見、8 個のエコシステム。
- cargo
- composer
- gem
- golang
- hex
- npm
- pub
- pypi
公式の記述と食い違うもの
ここでの思い込みは噂ではなく、プロジェクト自身のドキュメント、あるいはそれが実装する仕様に書かれていることです。どちらも確認できます。引用はリンク一つ、測定はコンテナ実行一回です。
-
cargoserde 1.0.229, serde_json 1.0.151
信じられていることserde's container-attribute documentation says deny_unknown_fields “is not supported in combination with flatten, neither on the outer struct nor on the flattened field”, which reads as a pair the derive will refuse.
測定結果serde_derive implements the pair: rustc compiles a struct carrying both with an empty stderr — no error, no warning — and the ordinary case works, since a flattened struct is accepted and only genuine leftovers are rejected. What does break is narrower than the note: a flattened map beside deny_unknown_fields can never collect a key, because every key it was meant to catch is reported unknown instead; and a struct carrying deny_unknown_fields accepts, once flattened into another struct, the same key it rejects when it stands alone.
それを証明する contract · 0c0a0329b6b2 出典: serde.rs container attributes
-
npmjose 6.2.8, Node 22
信じられていることRFC 7518 §3.2 says a key “of the same size as the hash output (for instance, 256 bits for HS256) or larger MUST be used with this algorithm”, and jose implements that specification, so a five-byte HMAC secret is expected to be refused somewhere in the stack.
測定結果jose signs and verifies an HS256 token with a five-byte key and reports nothing, and WebCrypto underneath imports the same 40-bit HMAC key just as willingly, so nothing below the library catches a weak secret either.
-
npm@modelcontextprotocol/sdk 1.30.0, protocol 2025-11-25, Node 22
信じられていることthe MCP specification splits tool failures into two channels and puts “Unknown tools” in the first one — “Protocol Errors: Standard JSON-RPC errors” — printing the example as an error object with code -32602, so a call naming a tool that does not exist should arrive as a JSON-RPC error.
測定結果the SDK's own McpServer answers it as a successful response instead: await client.callTool({name: "no_such_tool", arguments: {}}) resolves, its isError is true, and the -32602 arrives inside the text — “MCP error -32602: Tool no_such_tool not found” — so the code is prose rather than an error object, and a caller that detects failure by catching sees none. The contradiction is that one bullet and no more — the same page assigns input validation errors to the isError channel by design, which is where they arrive — and the same server does reject with a real McpError carrying code -32602 for an unregistered resource URI, so which channel a failure uses is decided by the handler it reached rather than by the kind of failure it is.
それを証明する contract · d133f23612d4 出典: MCP specification 2025-11-25, tools
広く信じられているが、測ると違うもの
ここでの思い込みは移行ガイド、隣のライブラリから持ち込んだ習慣、あるいは皆が繰り返している話です。測定の仕方は上と同じです。
-
npmbcryptjs 3.0.3, Node 22
信じられていることa password hash covers the whole password.
測定結果bcrypt truncates at 72 bytes and neither hashSync nor compareSync reports it: a second, different password sharing the first one's 72-byte prefix verifies against that hash, and so does the bare prefix, while 71 bytes does not; the limit is bytes, so 36 accented characters survive and 37 lose their tail, and bcryptjs's own truncates() is a separate call you have to make yourself.
-
npmnode:crypto scryptSync, Node 22
信じられていることthe memory scrypt needs is 128 * N * r — the figure Node's own documentation quotes, hedged as “It is an error when (approximately) 128 * N * r > maxmem” — so budgeting exactly that raises the cost safely.
測定結果budgeting exactly 128 * N * r is rejected with ERR_CRYPTO_INVALID_SCRYPT_PARAMS; the accepted minimum is exactly 128 * r * (N + p + 2), pinned to the byte at six parameter sets that vary N, r and p independently — 3072 bytes above the quoted figure, at the defaults and at N=32768 alike, and at N=32768 the quoted figure is exactly the 32 MiB default maxmem, so the next cost step looks like it just fits and does not.
-
pypipolars 1.43.2, Python 3.12
信じられていることdf[mask] filters rows, the way it does in pandas.
測定結果a boolean mask in brackets selects COLUMNS: against a frame whose column count differs from the mask length it raises ValueError, and against a frame with as many columns as the mask has rows — the shape of most test fixtures — the length check passes and it returns the wrong columns with no error at all.
-
composermonolog/monolog 3.9.0, PHP 8
信じられていることMonolog 3 removed the integer level constants, so an upgrade means replacing every Logger::WARNING.
測定結果Logger::WARNING is still defined, still 300, still equal to Level::Warning->value, and addRecord still accepts an int; what actually breaks is is_array($record), because a record is now a LogRecord object that keeps answering $record['message'] — so the migration reads as finished while the array branches quietly stop being taken.
-
npmlightningcss 1.33.0, npm 10, Alpine
信じられていることnpm installs only the right native package on Alpine, because each platform package declares the libc it was built for.
測定結果npm ci installed BOTH linux-x64 variants, glibc and musl, because the lockfile this npm 10 image wrote records os and cpu on every optional entry and never libc — every darwin, win32 and arm64 package was correctly skipped, and the unusable glibc build is a full second copy of the 10 MB addon on disk. This is npm's lockfile writer, not lightningcss, and it is fixed upstream: npm records libc from 11.11.0 on, so the repair is regenerating the lockfile rather than upgrading the npm that reads it.
-
npmesbuild 0.25.12, Alpine
信じられていることrunning esbuild on Alpine means installing its musl-specific build.
測定結果there is no musl build to install: none of esbuild's optional platform dependencies mentions musl, the linux-x64 package npm picks declares no libc constraint, and its binary has no ELF interpreter at all — read from the program headers on a musl image where node itself names musl's loader.
-
npmjose 6.2.8, Node 22
信じられていることcatching JWTClaimValidationFailed handles the claim checks jwtVerify performs.
測定結果JWTExpired is a sibling of JWTClaimValidationFailed rather than a subclass, so every expired token falls past that clause into the generic branch, while a not-yet-valid nbf — checked without being asked — is caught by it.
-
npmzod 4.4.3, Node 22
信じられていることz.coerce.number() parses a numeric string, rejecting what is not a number.
測定結果it is Number(input) followed by the number check, not a numeric parser, so "", " ", null, false and [] are all accepted and arrive as 0 — an empty form field or a null column silently becomes zero — while "1e999" is rejected, because Number() overflows it to Infinity.
-
npmvitest 4.1.10, Node 22
信じられていることa forgotten await on expect(...).rejects makes the test pass while asserting nothing.
測定結果which half is true depends on the test function: in a sync one the forgotten await still fails the test and carries the real rejection message, and in an async one the assertion settles first, so the test is reported passed and the failure becomes the run's single unhandled error — green test, red run, and any tooling reading only test states calls it a pass.
-
npmbun:sqlite, Bun 1.3.14
信じられていることthe options argument to new Database(path, options) overrides defaults.
測定結果it replaces the open flags outright, so new Database(":memory:", {}) throws SQLiteError SQLITE_MISUSE where new Database(":memory:") works, and { create: false } lands on the same zero flags — only a true access mode or a strict/safeIntegers key puts the default back.
-
composerguzzlehttp/guzzle 8.0.2, PHP 8
信じられていることif ($e->hasResponse()) { $e->getResponse(); } is how you read the response off a Guzzle RequestException.
測定結果on guzzle 8 neither method is declared on RequestException — getResponse moved down to ResponseException with a non-nullable return type, and hasResponse is declared on neither class — so the guzzle 7 idiom is a fatal “Call to undefined method …::getResponse()”, not a deprecation.
-
gemjson 2.9.1, Ruby 3
信じられていることJSON.dump is JSON.generate under another name.
測定結果JSON.dump defaults to allow_nan, so it writes {"ratio":NaN} — a document JSON.parse refuses and only JSON.load will read back — while JSON.generate refuses the same float outright; JSON.load carries the matching asymmetry with allow_blank, returning nil for an empty string where JSON.parse raises.
-
hexElixir's built-in JSON vs jason 1.4.4
信じられていることdropping Jason for the JSON module in Elixir 1.18+ is a module rename.
測定結果the two encode the same payload byte for byte, but the decode error is JSON.DecodeError, so a rescue Jason.DecodeError clause compiles, still reads correctly and catches nothing; JSON.decode/1 returns a bare reason tuple rather than a struct, and JSON.decode/2 does not exist, so keys: :atoms has nowhere to go.
-
pubcollection 1.19.1, Dart 3
信じられていることtwo Lists holding the same values are equal.
測定結果== on a List, Map or Set is identity, and package:test's equals matcher deep-compares — so the assertion is green on exactly the values production calls unequal; a const collection is canonicalized and does compare equal, which is what makes the rule look inconsistent, and copying it loses the equality again.
-
golangshopspring/decimal 1.4.0, Go 1.26
信じられていることa decimal type is exact, which is the reason to reach for one.
測定結果Div is DivRound reading a mutable package-level global, decimal.DivisionPrecision, which defaults to 16 — so (1/3)*3 is 0.9999999999999999 and not 1, any dependency in the process can move the precision, and nothing at the call site says so; DivRound takes the precision as an argument and QuoRem is the one that keeps the remainder.
-
golangspf13/cobra 1.10.2, Go 1.26
信じられていることa cobra command tree with no SetArgs runs with no arguments.
測定結果with no SetArgs at all cobra parses os.Args[1:], so a command tree driven from a test binary parses that binary's own arguments and fails on a flag nobody wrote; the guard is c.args == nil, so SetArgs(nil) reopens the same fallback and only an empty non-nil slice means “no arguments”. cobra's one escape hatch keys on the program name being exactly cobra.test, which saves its own tests and nobody else's.
-
pypiorjson 3.11.9, Python 3.12
信じられていることorjson's README says “JSONEncodeError is a subclass of TypeError”, which reads as a narrower class, so except orjson.JSONEncodeError looks tighter than except TypeError.
測定結果orjson.JSONEncodeError is TypeError — the same object, so the README's sentence holds only in the sense that every class is a subclass of itself — and the two clauses are therefore identical: either one swallows every TypeError raised in the block, not only the encoder's. JSONDecodeError, by contrast, really is a distinct subclass of json.JSONDecodeError.
-
pypiattrs 26.1.0 vs dataclasses, CPython 3.12
信じられていること@dataclass(slots=True) is the stdlib equivalent of an attrs slotted class.
測定結果on CPython 3.12 the stdlib leaves the __class__ closure cell pointing at the class it discarded, so zero-argument super() inside a slotted dataclass raises TypeError at call time — not at definition — while the identical attrs class works because attrs rebinds the cell; weakref.ref also raises on the slotted dataclass and not on the attrs one.
-
pypifreezegun 1.5.5, Python 3.12
信じられていることfreeze_time moves the wall clock, so monotonic timers are unaffected.
測定結果it patches time.monotonic and time.perf_counter to the same frozen wall clock, so a duration measured across the boundary of the freeze comes out about thirty years long, and moving the frozen clock backwards makes time.monotonic() go backwards — a deadline written as monotonic() + timeout is never reached.
-
cargoaxum 0.8.9
信じられていることaxum answers a bad JSON body with 422.
測定結果it answers with three statuses and only one of them is 422: syntactically broken JSON is 400, a body that parses but does not fit the target type — a wrong field type, or a required field left out — is 422, and a request with no Content-Type at all is 415 carrying the rejection message where the handler's 201 would have been, so a single assertion for “bad input” is wrong on two of the three. The 400's rejection body is text/plain rather than JSON; and the Content-Type check is not string equality — the header is parsed and its type has to be application, so application/json; charset=utf-8 and application/vnd.csx+json are accepted while text/json is refused exactly like a header that was never sent.
-
cargoonce_cell 1.21.4 vs std
信じられていることstd absorbed once_cell, so the dependency can go.
測定結果almost — and the remainder is two methods, both of them the fallible ones. Compiled with the rustc this sample was measured on, LazyLock::force_mut, DerefMut on LazyLock and OnceLock::wait all build, so the reasons usually quoted for keeping the crate are out of date, and std::cell::OnceCell and LazyCell cover its unsync half. OnceLock::get_or_try_init and try_insert do not build: both are E0658, the first behind once_cell_try with tracking issue 109737, the second behind once_cell_try_insert — so a fallible initialiser has no std spelling, and the get-then-set stand-in written in their place is not equivalent, because 16 threads racing it run the initialiser 16 times where get_or_init runs it once.
-
golanggorm.io/driver/sqlite 1.6.0 over mattn/go-sqlite3 1.14.49, Go 1.26
信じられていることa cgo package cannot be built with CGO_ENABLED=0, so the build catches it.
測定結果go-sqlite3 compiles a stub instead: the import builds, the binary links, and the stub still registers the database/sql driver name sqlite3, so finding that name in sql.Drivers() proves nothing about the driver working. The first thing that connects is what fails, with an error naming CGO_ENABLED=0 and saying it “requires cgo to work”, and database/sql defers even that: sql.Open only records the driver name and returns a nil error, and Ping is where “This is a stub” surfaces. So the build is green and the first connection is where it breaks.
-
golangspf13/viper 1.21.0, Go 1.26
信じられていることAutomaticEnv feeds the environment into Unmarshal the way it feeds Get.
測定結果Unmarshal enumerates AllKeys and reads each key it finds, and AutomaticEnv contributes no keys to that list — it cannot, since it would have to guess names — so with CSX_LOG_LEVEL exported, GetString("log.level") returns env while AllSettings is empty and the struct field stays "", with no error anywhere to say the two disagree. The repair is making the key enumerable: either a SetDefault that never wins the lookup it just enabled, or viper.ExperimentalBindStruct, which takes the key list from the destination struct.
-
composersymfony/console 8.1.4, PHP 8
信じられていることpassing --no-interaction to CommandTester::execute turns the prompts off, the way it does on the command line.
測定結果what interprets that flag is the application run, and a CommandTester is not one, so under it the option binds and does nothing: getOption('no-interaction') is true and the question is asked anyway. -v is the same dead end — bound true while the output stays at VERBOSITY_NORMAL and the verbose writeln prints nothing — and what the tester does read are execute()'s own interactive and verbosity options, or the same command driven through ApplicationTester, where both flags mean what they say.
-
pubshelf_router 1.1.4, Dart 3
信じられていることa route handler's parameter names say which capture each one receives.
測定結果captures are applied positionally in the order the route pattern declares them and the closure's parameter names are never read, so a handler written (Request request, String id, String org) against /orgs/<org>/users/<id> receives the org capture in id: /orgs/acme/users/u42 answers “acme/u42” where the names promise “u42/acme”, and nothing reports it. The count is not checked at registration either — one argument too many registers cleanly and becomes a NoSuchMethodError on the first request that matches.
-
hexecto 3.14.1
信じられていることan empty string in the params either arrives as an empty string or clears the field.
測定結果cast compares it against empty_values — [""], compared after trimming, so a whitespace-only string counts too, while a value that survives the check is stored untrimmed — and substitutes the field's declared default, which is nil only for a field that has none: sending "" for a field defaulting to "member" over a stored "admin" writes "member", so an empty form field demotes rather than clears. When the substituted default equals the data the key is absent from changes rather than present and empty, which is why the debugger shows nothing there and validate_required reports “can't be blank” for a param that did arrive.
ここの行を確かめる方法
サンプルを開き、contract を読み、実行してください。contract はそのサンプル自身のテストです。ネットワークを切った固定コンテナで実行され、その実行の署名済みレシートがネットワークに保存されます。ここに書かれていることはライブラリについての解釈ではなく、ライブラリが実際に行ったことだけに基づいています。
サンプルが公開されていることを確認できなかった発見はこのページに載せていません。一覧がサンプル全体より短いのは意図的です。