Skip to content

fix Exception message for incorrect dict literal is too wide #893#2330

Open
asukaminato0721 wants to merge 5 commits into
facebook:mainfrom
asukaminato0721:893
Open

fix Exception message for incorrect dict literal is too wide #893#2330
asukaminato0721 wants to merge 5 commits into
facebook:mainfrom
asukaminato0721:893

Conversation

@asukaminato0721

@asukaminato0721 asukaminato0721 commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #893

Narrowed dict-literal assignment errors to the first mismatching key/value/unpack when a dict[...] hint is present, so the caret lands on the wrong entry instead of the whole { ... }.

to decide: only show first error for now. Maybe show full error? But will it too noisy?

Test Plan

Added a unit test that asserts the error range points at the offending value literal.

@meta-cla meta-cla Bot added the cla signed label Feb 6, 2026
@asukaminato0721

Copy link
Copy Markdown
Contributor Author
ERROR `dict[int | str, int | str]` is not assignable to `dict[str, str]` [bad-assignment]
 --> /tmp/tmp/main.py:3:5
  |
3 |     1: "2",
  |     ^
  |
 INFO 1 error
@asukaminato0721 asukaminato0721 marked this pull request as ready for review February 6, 2026 03:56
Copilot AI review requested due to automatic review settings February 6, 2026 03:56

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes issue #893 by narrowing dict literal type mismatch errors to point at the specific problematic key, value, or unpacked mapping instead of the entire dict literal. This improves the developer experience by making it easier to identify which element in a dict literal is causing a type error.

Changes:

  • Added dict_literal_error_range function to identify the first mismatching element in a dict literal
  • Added test case to verify error ranges point to the specific value causing the error
  • Integrated the new function into the type checking flow to provide narrowed error ranges

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
pyrefly/lib/alt/expr.rs Implements dict_literal_error_range function that re-infers dict elements to find the first type mismatch and integrates it into type checking
pyrefly/lib/test/dict.rs Adds test case verifying that dict literal errors point to the specific incorrect value

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pyrefly/lib/test/dict.rs
Comment on lines +59 to +77
#[test]
fn test_dict_literal_error_range_points_to_value() {
util::init_test();
let code = r#"x: dict[str, str] = {
"1": 2,
}
"#;
let (handle, state) = util::mk_state(code);
let errors = state
.transaction()
.get_errors([&handle])
.collect_errors()
.shown;
assert_eq!(errors.len(), 1);
let err = &errors[0];
let value_offset = code.find("2").expect("missing dict value literal");
let expected_start = TextSize::try_from(value_offset).unwrap();
assert_eq!(err.range().start(), expected_start);
}

Copilot AI Feb 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test only covers the case where the value type is wrong. Consider adding additional test cases to cover: 1) when the key type is wrong, 2) when both key and value types are wrong (should point to key as it's checked first), 3) when an unpacked dict has incompatible types, and 4) when there are multiple errors in the dict but only the first one should be reported.

Copilot uses AI. Check for mistakes.
Comment thread pyrefly/lib/alt/expr.rs Outdated
Comment thread pyrefly/lib/alt/expr.rs Outdated
Comment thread pyrefly/lib/test/dict.rs
let err = &errors[0];
let value_offset = code.find("2").expect("missing dict value literal");
let expected_start = TextSize::try_from(value_offset).unwrap();
assert_eq!(err.range().start(), expected_start);

Copilot AI Feb 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test only checks that the error starts at the correct position but doesn't verify the end position of the error range. Consider also asserting on the end position to ensure the error range only covers the problematic value (the literal "2") and not additional characters.

Copilot uses AI. Check for mistakes.
@github-actions

This comment has been minimized.

@asukaminato0721 asukaminato0721 marked this pull request as draft February 6, 2026 04:57
@asukaminato0721 asukaminato0721 marked this pull request as ready for review February 6, 2026 09:11
@github-actions

This comment has been minimized.

@asukaminato0721 asukaminato0721 marked this pull request as draft February 6, 2026 09:40
@asukaminato0721 asukaminato0721 marked this pull request as ready for review February 6, 2026 09:59
@github-actions

This comment has been minimized.

@rchen152 rchen152 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know why the mypy_primer results changed? That seems concerning to me; I wouldn't expect an improvement to an error message to affect what errors are reported.

@github-actions

This comment has been minimized.

@asukaminato0721

Copy link
Copy Markdown
Contributor Author

In dict_items_infer we call check_type on each key/value and record those errors on the item ranges. That can increase the count and change message text

For example, this shows the range is smaller.

- ERROR static_frame/test/unit/test_frame.py:1007:43-62
+ ERROR static_frame/test/unit/test_frame.py:1007:44-45

In multiline dicts, the error now lands on the offending key/value line, not on the assignment line. Any # type: ignore or pyrefly suppression on the assignment line no longer applies, so extra errors appear.

@meta-codesync

meta-codesync Bot commented Feb 12, 2026

Copy link
Copy Markdown
Contributor

@rchen152 has imported this pull request. If you are a Meta employee, you can view this in D93159297.

@rchen152 rchen152 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay in getting you a more in-depth review of this PR. The mypy_primer change being caused by the error range changing makes sense, thanks.

Comment thread pyrefly/lib/alt/expr.rs Outdated
@github-actions

This comment has been minimized.

@asukaminato0721

Copy link
Copy Markdown
Contributor Author

well, this is a bit ... verbose but more precise.

@github-actions

This comment has been minimized.

@rchen152 rchen152 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! This looks like a more promising direction.

Some of the new errors in mypy_primer look like false positives. For example, there's a new error at https://github.com/scrapy/scrapy/blob/ccfa052fa19f712355fb17b863e8ff77f34ff3ac/scrapy/downloadermiddlewares/cookies.py#L188C1-L188C76 but no corresponding old error or # type: ignore. Looking at the signature of Response.__init__: https://github.com/scrapy/scrapy/blob/ccfa052fa19f712355fb17b863e8ff77f34ff3ac/scrapy/http/response/__init__.py#L63, we seem to be matching the headers argument against Iterable[tuple[AnyStr, Any]] and not considering the other options in the union.

I'm suspicious that this may be due to a preexisting bug in how union hints are decomposed (#793). Maybe we need to exclude union hints from this new logic until that bug has been resolved.

Comment thread pyrefly/lib/alt/expr.rs Outdated
Comment thread pyrefly/lib/alt/expr.rs Outdated
Comment thread pyrefly/lib/alt/expr.rs Outdated
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions github-actions Bot removed the stale label Mar 24, 2026
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown

Diff from mypy_primer, showing the effect of this PR on open source code:

rotki (https://github.com/rotki/rotki)
- ERROR rotkehlchen/chain/aggregator.py:345:129-348:10: `dict[SupportedBlockchain, (self: Self@ChainsAggregator, blockchain: SUPPORTED_SUBSTRATE_CHAINS_TYPE, append_or_remove: Literal['append', 'remove']) -> None]` is not assignable to attribute `chain_modify_init` with type `dict[SupportedBlockchain, (SupportedBlockchain, Literal['append', 'remove']) -> None]` [bad-assignment]
- ERROR rotkehlchen/chain/aggregator.py:349:121-351:10: `dict[SupportedBlockchain, (self: Self@ChainsAggregator, blockchain: Literal[SupportedBlockchain.ETHEREUM], address: ChecksumAddress) -> None]` is not assignable to attribute `chain_modify_append` with type `dict[SupportedBlockchain, (SupportedBlockchain, BlockchainAddress) -> None]` [bad-assignment]
- ERROR rotkehlchen/chain/aggregator.py:352:121-354:10: `dict[SupportedBlockchain, (self: Self@ChainsAggregator, blockchain: Literal[SupportedBlockchain.ETHEREUM], address: ChecksumAddress) -> None]` is not assignable to attribute `chain_modify_remove` with type `dict[SupportedBlockchain, (SupportedBlockchain, BlockchainAddress) -> None]` [bad-assignment]
+ ERROR rotkehlchen/tests/api/test_history_base_entry.py:1433:36-37: `int` is not assignable to `str` [bad-assignment]

jax (https://github.com/google/jax)
+ ERROR jax/_src/internal_test_util/export_back_compat_test_data/cpu_triangular_solve_blas_trsm.py:119:12-125:73: `tuple[ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]], ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]]]` is not assignable to `bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]` [bad-assignment]
- ERROR jax/_src/internal_test_util/export_back_compat_test_data/cpu_triangular_solve_blas_trsm.py:114:26-151:2: Cannot set item in `dict[str, dict[str, bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]]]` [unsupported-operation]
+ ERROR jax/_src/internal_test_util/export_back_compat_test_data/cpu_triangular_solve_blas_trsm.py:126:22-133:65: `tuple[ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]]]` is not assignable to `bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]` [bad-assignment]
- ERROR jax/_src/internal_test_util/export_back_compat_test_data/cpu_tridiagonal_solve_lapack_gtsv.py:158:26-233:2: Cannot set item in `dict[str, dict[str, bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]]]` [unsupported-operation]
- ERROR jax/_src/internal_test_util/export_back_compat_test_data/cuda_cholesky_solver_potrf.py:160:26-231:2: Cannot set item in `dict[str, dict[str, bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]]]` [unsupported-operation]
- ERROR jax/_src/internal_test_util/export_back_compat_test_data/cuda_eigh_cusolver_syev.py:183:26-271:2: Cannot set item in `dict[str, dict[str, bytes | date | int | list[str] | str | tuple[()] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]]]` [unsupported-operation]
- ERROR jax/_src/internal_test_util/export_back_compat_test_data/cuda_lu_cusolver_getrf.py:115:26-157:2: Cannot set item in `dict[str, dict[str, bytes | date | int | list[str] | str | tuple[()] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[signedinteger[_32Bit]]], ndarray[tuple[Any, ...], dtype[signedinteger[_32Bit]]]]]]` [unsupported-operation]
- ERROR jax/_src/internal_test_util/export_back_compat_test_data/cuda_qr_cusolver_geqrf.py:158:26-218:2: Cannot set item in `dict[str, dict[str, bytes | date | int | list[str] | str | tuple[()] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]]]` [unsupported-operation]
+ ERROR jax/_src/internal_test_util/export_back_compat_test_data/cpu_tridiagonal_solve_lapack_gtsv.py:163:12-189:55: `tuple[ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]], ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]], ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]], ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]]]` is not assignable to `bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]` [bad-assignment]
+ ERROR jax/_src/internal_test_util/export_back_compat_test_data/cpu_tridiagonal_solve_lapack_gtsv.py:190:22-206:60: `tuple[ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]]]` is not assignable to `bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]` [bad-assignment]
+ ERROR jax/_src/internal_test_util/export_back_compat_test_data/cuda_cholesky_solver_potrf.py:165:12-172:77: `tuple[ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]]]` is not assignable to `bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]` [bad-assignment]
+ ERROR jax/_src/internal_test_util/export_back_compat_test_data/cuda_cholesky_solver_potrf.py:173:22-181:25: `tuple[ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]]]` is not assignable to `bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]` [bad-assignment]
+ ERROR jax/_src/internal_test_util/export_back_compat_test_data/cuda_eigh_cusolver_syev.py:189:22-197:22: `tuple[ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]` is not assignable to `bytes | date | int | list[str] | str | tuple[()] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]` [bad-assignment]
+ ERROR jax/_src/internal_test_util/export_back_compat_test_data/cuda_lu_cusolver_getrf.py:121:22-123:130: `tuple[ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]], ndarray[tuple[Any, ...], dtype[signedinteger[_32Bit]]], ndarray[tuple[Any, ...], dtype[signedinteger[_32Bit]]]]` is not assignable to `bytes | date | int | list[str] | str | tuple[()] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[signedinteger[_32Bit]]], ndarray[tuple[Any, ...], dtype[signedinteger[_32Bit]]]]` [bad-assignment]
+ ERROR jax/_src/internal_test_util/export_back_compat_test_data/cuda_qr_cusolver_geqrf.py:164:22-178:24: `tuple[ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]], ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]]]` is not assignable to `bytes | date | int | list[str] | str | tuple[()] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]` [bad-assignment]
- ERROR jax/_src/internal_test_util/export_back_compat_test_data/cuda_svd_cusolver_gesvd.py:187:36-283:2: Cannot set item in `dict[str, dict[str, bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]]]` [unsupported-operation]
+ ERROR jax/_src/internal_test_util/export_back_compat_test_data/cuda_svd_cusolver_gesvd.py:192:12-209:25: `tuple[ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]]]` is not assignable to `bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]` [bad-assignment]
+ ERROR jax/_src/internal_test_util/export_back_compat_test_data/cuda_svd_cusolver_gesvd.py:210:22-245:24: `tuple[ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]]]` is not assignable to `bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]` [bad-assignment]
- ERROR jax/_src/internal_test_util/export_back_compat_test_data/cuda_tridiagonal_cusolver_sytrd.py:182:26-269:2: Cannot set item in `dict[str, dict[str, bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]]]` [unsupported-operation]
+ ERROR jax/_src/internal_test_util/export_back_compat_test_data/cuda_tridiagonal_cusolver_sytrd.py:187:12-204:25: `tuple[ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]]]` is not assignable to `bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]` [bad-assignment]
- ERROR jax/_src/internal_test_util/export_back_compat_test_data/gpu_eigh_solver_syev.py:271:26-406:2: Cannot set item in `dict[str, dict[str, bytes | date | int | list[str] | str | tuple[()] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]]]` [unsupported-operation]
- ERROR jax/_src/internal_test_util/export_back_compat_test_data/rocm_cholesky_solver_potrf.py:160:26-231:2: Cannot set item in `dict[str, dict[str, bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]]]` [unsupported-operation]
- ERROR jax/_src/internal_test_util/export_back_compat_test_data/rocm_lu_rocsolver_getrf.py:125:26-170:2: Cannot set item in `dict[str, dict[str, bytes | date | int | list[str] | str | tuple[()] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[signedinteger[_32Bit]]], ndarray[tuple[Any, ...], dtype[signedinteger[_32Bit]]]]]]` [unsupported-operation]
- ERROR jax/_src/internal_test_util/export_back_compat_test_data/rocm_qr_hipsolver_geqrf.py:156:26-213:2: Cannot set item in `dict[str, dict[str, bytes | date | int | list[str] | str | tuple[()] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]]]` [unsupported-operation]
+ ERROR jax/_src/internal_test_util/export_back_compat_test_data/cuda_tridiagonal_cusolver_sytrd.py:205:22-227:51: `tuple[ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]]]` is not assignable to `bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]` [bad-assignment]
+ ERROR jax/_src/internal_test_util/export_back_compat_test_data/gpu_eigh_solver_syev.py:277:20-306:4: `tuple[ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]` is not assignable to `bytes | date | int | list[str] | str | tuple[()] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]` [bad-assignment]
+ ERROR jax/_src/internal_test_util/export_back_compat_test_data/rocm_cholesky_solver_potrf.py:165:12-173:25: `tuple[ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]]]` is not assignable to `bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]` [bad-assignment]
+ ERROR jax/_src/internal_test_util/export_back_compat_test_data/rocm_cholesky_solver_potrf.py:174:22-181:75: `tuple[ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]]]` is not assignable to `bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]` [bad-assignment]
+ ERROR jax/_src/internal_test_util/export_back_compat_test_data/rocm_lu_rocsolver_getrf.py:131:22-133:130: `tuple[ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]], ndarray[tuple[Any, ...], dtype[signedinteger[_32Bit]]], ndarray[tuple[Any, ...], dtype[signedinteger[_32Bit]]]]` is not assignable to `bytes | date | int | list[str] | str | tuple[()] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[signedinteger[_32Bit]]], ndarray[tuple[Any, ...], dtype[signedinteger[_32Bit]]]]` [bad-assignment]
+ ERROR jax/_src/internal_test_util/export_back_compat_test_data/rocm_qr_hipsolver_geqrf.py:162:22-176:24: `tuple[ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]], ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]]]` is not assignable to `bytes | date | int | list[str] | str | tuple[()] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]` [bad-assignment]
- ERROR jax/_src/internal_test_util/export_back_compat_test_data/rocm_svd_hipsolver_gesvd.py:192:36-289:2: Cannot set item in `dict[str, dict[str, bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]]]` [unsupported-operation]
+ ERROR jax/_src/internal_test_util/export_back_compat_test_data/rocm_svd_hipsolver_gesvd.py:197:12-214:25: `tuple[ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]]]` is not assignable to `bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]` [bad-assignment]
+ ERROR jax/_src/internal_test_util/export_back_compat_test_data/rocm_svd_hipsolver_gesvd.py:215:22-250:24: `tuple[ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]]]` is not assignable to `bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]` [bad-assignment]
- ERROR jax/_src/internal_test_util/export_back_compat_test_data/rocm_tridiagonal_hipsolver_sytrd.py:178:26-262:2: Cannot set item in `dict[str, dict[str, bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]]]` [unsupported-operation]
+ ERROR jax/_src/internal_test_util/export_back_compat_test_data/rocm_tridiagonal_hipsolver_sytrd.py:183:12-200:25: `tuple[ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]]]` is not assignable to `bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]` [bad-assignment]
+ ERROR jax/_src/internal_test_util/export_back_compat_test_data/rocm_tridiagonal_hipsolver_sytrd.py:201:22-224:51: `tuple[ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[complexfloating[_32Bit, _32Bit]]]]` is not assignable to `bytes | date | int | list[str] | str | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]] | tuple[ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]], ndarray[tuple[Any, ...], dtype[floating[_32Bit]]]]` [bad-assignment]
+ ERROR jax/experimental/colocated_python/serialization.py:245:10-36: `type[NamedSharding]` is not assignable to `type[Mesh]` [bad-assignment]
+ ERROR jax/experimental/colocated_python/serialization.py:246:10-20: `type[DeviceList]` is not assignable to `type[Mesh]` [bad-assignment]
+ ERROR jax/experimental/colocated_python/serialization.py:247:10-43: `type[SingleDeviceSharding]` is not assignable to `type[Mesh]` [bad-assignment]

spark (https://github.com/apache/spark)
- ERROR python/pyspark/pandas/tests/groupby/test_describe.py:33:22-76: Argument `dict[str, list[int] | list[str]]` is not assignable to parameter `object` with type `dict[str, list[int]]` in function `list.append` [bad-argument-type]
+ ERROR python/pyspark/pandas/tests/groupby/test_describe.py:33:28-43: `list[str]` is not assignable to `list[int]` [bad-assignment]
- ERROR python/pyspark/pandas/tests/groupby/test_describe.py:69:22-82: Argument `dict[str, list[int] | list[str]]` is not assignable to parameter `object` with type `dict[str, list[str]]` in function `list.append` [bad-argument-type]
+ ERROR python/pyspark/pandas/tests/groupby/test_describe.py:69:50-59: `list[int]` is not assignable to `list[str]` [bad-assignment]

cwltool (https://github.com/common-workflow-language/cwltool)
- ERROR cwltool/command_line_tool.py:850:78-98: `dict[str, list[MutableMapping[str, CWLOutputType | None] | MutableSequence[CWLOutputType | None] | bool | float | int | str | CWLDirectoryType | CWLFileType] | list[Any]]` is not assignable to `dict[str, MutableMapping[str, MutableMapping[str, CWLOutputType | None] | MutableSequence[CWLOutputType | None] | bool | float | int | str | CWLDirectoryType | CWLFileType | None] | MutableSequence[int | str]]` [bad-assignment]
+ ERROR cwltool/command_line_tool.py:850:90-97: `list[MutableMapping[str, CWLOutputType | None] | MutableSequence[CWLOutputType | None] | bool | float | int | str | CWLDirectoryType | CWLFileType] | list[Any]` is not assignable to `MutableMapping[str, MutableMapping[str, CWLOutputType | None] | MutableSequence[CWLOutputType | None] | bool | float | int | str | CWLDirectoryType | CWLFileType | None] | MutableSequence[int | str]` [bad-assignment]
- ERROR tests/test_examples.py:80:15-88:6: `dict[str, dict[str, dict[str, @_ | None] | dict[str, bool] | dict[str, int] | dict[str, str]] | list[str]]` is not assignable to TypedDict key `inputs` with type `MutableMapping[str, MutableMapping[str, CWLOutputType | None] | MutableSequence[CWLOutputType | None] | bool | float | int | str | CWLDirectoryType | CWLFileType | None]` [bad-assignment]
+ ERROR tests/test_examples.py:81:16-86:10: `dict[str, dict[str, @_ | None] | dict[str, bool] | dict[str, int] | dict[str, str]]` is not assignable to `MutableMapping[str, CWLOutputType | None] | MutableSequence[CWLOutputType | None] | bool | float | int | str | CWLDirectoryType | CWLFileType | None` [bad-assignment]
+ ERROR tests/test_examples.py:87:16-26: `list[str]` is not assignable to `MutableMapping[str, CWLOutputType | None] | MutableSequence[CWLOutputType | None] | bool | float | int | str | CWLDirectoryType | CWLFileType | None` [bad-assignment]
- ERROR tests/test_examples.py:368:26-424:6: `dict[str, list[dict[str, list[dict[str, str | dict[str, str]]] | str | dict[str, list[dict[str, str | dict[str, str]] | dict[str, str | dict[str, list[dict[str, str]] | str]] | dict[str, str | dict[str, list[dict[str, list[dict[str, str]] | str]] | str]]] | str]]] | str]` is not assignable to `MutableMapping[str, MutableMapping[str, CWLOutputType | None] | MutableSequence[CWLOutputType | None] | bool | float | int | str | CWLDirectoryType | CWLFileType | None]` [bad-assignment]
+ ERROR tests/test_examples.py:370:18-423:10: `list[dict[str, list[dict[str, str | dict[str, str]]] | str | dict[str, list[dict[str, str | dict[str, str]] | dict[str, str | dict[str, list[dict[str, str]] | str]] | dict[str, str | dict[str, list[dict[str, list[dict[str, str]] | str]] | str]]] | str]]]` is not assignable to `MutableMapping[str, CWLOutputType | None] | MutableSequence[CWLOutputType | None] | bool | float | int | str | CWLDirectoryType | CWLFileType | None` [bad-assignment]
- ERROR tests/test_streaming.py:94:31-100:6: `dict[str, dict[str, bool | str]]` is not assignable to `MutableMapping[str, MutableMapping[str, CWLOutputType | None] | MutableSequence[CWLOutputType | None] | bool | float | int | str | CWLDirectoryType | CWLFileType | None]` [bad-assignment]
+ ERROR tests/test_streaming.py:95:16-99:10: `dict[str, bool | str]` is not assignable to `MutableMapping[str, CWLOutputType | None] | MutableSequence[CWLOutputType | None] | bool | float | int | str | CWLDirectoryType | CWLFileType | None` [bad-assignment]

cryptography (https://github.com/pyca/cryptography)
- ERROR tests/utils.py:419:17-425:18: Argument `dict[str, bytes | int | str]` is not assignable to parameter `object` with type `dict[str, int | str]` in function `list.append` [bad-argument-type]
+ ERROR tests/utils.py:424:28-54: `bytes` is not assignable to `int | str` [bad-assignment]

prefect (https://github.com/PrefectHQ/prefect)
+ ERROR src/integrations/prefect-docker/prefect_docker/images.py:65:23-33: `str` is not assignable to `dict[str, Any]` [bad-assignment]
- ERROR src/integrations/prefect-docker/prefect_docker/images.py:64:19-70:6: `dict[str, bool | dict[str, Any] | str | None]` is not assignable to variable `pull_kwargs` with type `dict[str, dict[str, Any]]` [bad-assignment]
+ ERROR src/integrations/prefect-docker/prefect_docker/images.py:66:16-19: `str | None` is not assignable to `dict[str, Any]` [bad-assignment]
+ ERROR src/integrations/prefect-docker/prefect_docker/images.py:67:21-29: `str | None` is not assignable to `dict[str, Any]` [bad-assignment]
+ ERROR src/integrations/prefect-docker/prefect_docker/images.py:68:21-29: `bool` is not assignable to `dict[str, Any]` [bad-assignment]
- ERROR src/prefect/server/events/jinja_filters.py:83:74-87:2: `dict[str, (text: str | None) -> str | None]` is not assignable to `dict[str, (Mapping[str, Any], Any) -> str | None]` [bad-assignment]

aiortc (https://github.com/aiortc/aiortc)
- ERROR src/aiortc/contrib/signaling.py:44:19-49:10: `dict[str, int | str | None]` is not assignable to variable `message` with type `dict[str, int | str]` [bad-assignment]
+ ERROR src/aiortc/contrib/signaling.py:46:19-29: `str | None` is not assignable to `int | str` [bad-assignment]
+ ERROR src/aiortc/contrib/signaling.py:47:22-39: `int | None` is not assignable to `int | str` [bad-assignment]

materialize (https://github.com/MaterializeInc/materialize)
+ ERROR test/balancerd/mzcompose.py:115:49-72: `str` is not assignable to `list[str]` [bad-assignment]
+ ERROR test/balancerd/mzcompose.py:137:49-70: `str` is not assignable to `list[str]` [bad-assignment]
+ ERROR test/balancerd/mzcompose.py:166:49-72: `str` is not assignable to `list[str]` [bad-assignment]
+ ERROR test/balancerd/mzcompose.py:177:49-76: `str` is not assignable to `list[str]` [bad-assignment]
+ ERROR test/balancerd/mzcompose.py:200:49-75: `str` is not assignable to `list[str]` [bad-assignment]
+ ERROR test/balancerd/mzcompose.py:294:53-79: `str` is not assignable to `list[str]` [bad-assignment]
+ ERROR test/balancerd/mzcompose.py:317:53-76: `str` is not assignable to `list[str]` [bad-assignment]
+ ERROR test/balancerd/mzcompose.py:691:53-76: `str` is not assignable to `list[str]` [bad-assignment]
+ ERROR test/balancerd/mzcompose.py:891:53-80: `str` is not assignable to `list[str]` [bad-assignment]
+ ERROR test/balancerd/mzcompose.py:909:53-79: `str` is not assignable to `list[str]` [bad-assignment]

apprise (https://github.com/caronc/apprise)
- ERROR apprise/plugins/fluxer.py:788:34-796:10: `dict[str, NotifyFormat | OverflowMode | bool | float | str | None]` is not assignable to `dict[str, str]` [bad-assignment]
+ ERROR apprise/plugins/fluxer.py:789:21-30: `NotifyFormat | OverflowMode | bool | float | str | None` is not assignable to `str` [bad-assignment]
- ERROR tests/test_attach_http.py:147:45-150:10: `dict[str, int | str]` is not assignable to `dict[str, str]` [bad-assignment]
+ ERROR tests/test_attach_http.py:148:31-44: `int` is not assignable to `str` [bad-assignment]

mongo-python-driver (https://github.com/mongodb/mongo-python-driver)
- ERROR pymongo/asynchronous/auth.py:365:69-374:2: `dict[str, ((credentials: MongoCredential, conn: AsyncConnection) -> Coroutine[Unknown, Unknown, None]) | ((credentials: MongoCredential, conn: AsyncConnection, reauthenticate: bool) -> Coroutine[Unknown, Unknown, Mapping[str, Any] | None]) | partial[Coroutine[Unknown, Unknown, None]]]` is not assignable to `Mapping[str, (...) -> Coroutine[Any, Any, None]]` [bad-assignment]
- ERROR pymongo/synchronous/auth.py:360:48-369:2: `dict[str, ((credentials: MongoCredential, conn: Connection) -> None) | ((credentials: MongoCredential, conn: Connection, reauthenticate: bool) -> Mapping[str, Any] | None) | partial[None]]` is not assignable to `Mapping[str, (...) -> None]` [bad-assignment]

pandera (https://github.com/pandera-dev/pandera)
- ERROR tests/pandas/test_decorators.py:1141:9-1144:10: Argument `dict[str, pandas.core.frame.DataFrame]` is not assignable to parameter `dict_of_df` with type `dict[str, pandera.typing.pandas.DataFrame[OnlyZeroesSchema]]` in function `validate_dict` [bad-argument-type]
- ERROR tests/pandas/test_decorators.py:1149:13-1152:14: Argument `dict[str, pandas.core.frame.DataFrame]` is not assignable to parameter `dict_of_df` with type `dict[str, pandera.typing.pandas.DataFrame[OnlyZeroesSchema]]` in function `validate_dict` [bad-argument-type]
- ERROR tests/pandas/test_decorators.py:1157:13-1160:14: Argument `dict[str, pandas.core.frame.DataFrame]` is not assignable to parameter `dict_of_df` with type `dict[str, pandera.typing.pandas.DataFrame[OnlyZeroesSchema]]` in function `validate_dict` [bad-argument-type]
- ERROR tests/pandas/test_decorators.py:1175:13-1178:14: Argument `dict[str, pandas.core.frame.DataFrame]` is not assignable to parameter `dict_of_df` with type `dict[str, pandera.typing.pandas.DataFrame[OnlyZeroesSchema]]` in function `validate_dict_wrong_outputs` [bad-argument-type]
- ERROR tests/pandas/test_decorators.py:1202:9-1205:10: Argument `dict[str, pandas.core.frame.DataFrame]` is not assignable to parameter `dict_of_union_df` with type `dict[str, pandera.typing.pandas.DataFrame[OnlyOnesSchema] | pandera.typing.pandas.DataFrame[OnlyZeroesSchema]]` in function `validate_dict` [bad-argument-type]
- ERROR tests/pandas/test_decorators.py:1210:13-1213:14: Argument `dict[str, pandas.core.frame.DataFrame]` is not assignable to parameter `dict_of_union_df` with type `dict[str, pandera.typing.pandas.DataFrame[OnlyOnesSchema] | pandera.typing.pandas.DataFrame[OnlyZeroesSchema]]` in function `validate_dict` [bad-argument-type]
- ERROR tests/pandas/test_decorators.py:1218:13-1221:14: Argument `dict[str, pandas.core.frame.DataFrame]` is not assignable to parameter `dict_of_union_df` with type `dict[str, pandera.typing.pandas.DataFrame[OnlyOnesSchema] | pandera.typing.pandas.DataFrame[OnlyZeroesSchema]]` in function `validate_dict` [bad-argument-type]
- ERROR tests/pandas/test_decorators.py:1244:13-1247:14: Argument `dict[str, pandas.core.frame.DataFrame]` is not assignable to parameter `dict_of_union_df` with type `dict[str, pandera.typing.pandas.DataFrame[OnlyOnesSchema] | pandera.typing.pandas.DataFrame[OnlyZeroesSchema]]` in function `validate_dict_wrong_outputs` [bad-argument-type]
+ ERROR tests/pandas/test_decorators.py:1142:25-52: `pandas.core.frame.DataFrame` is not assignable to `pandera.typing.pandas.DataFrame[OnlyZeroesSchema]` [bad-assignment]
+ ERROR tests/pandas/test_decorators.py:1143:25-52: `pandas.core.frame.DataFrame` is not assignable to `pandera.typing.pandas.DataFrame[OnlyZeroesSchema]` [bad-assignment]
+ ERROR tests/pandas/test_decorators.py:1150:29-56: `pandas.core.frame.DataFrame` is not assignable to `pandera.typing.pandas.DataFrame[OnlyZeroesSchema]` [bad-assignment]
+ ERROR tests/pandas/test_decorators.py:1151:29-56: `pandas.core.frame.DataFrame` is not assignable to `pandera.typing.pandas.DataFrame[OnlyZeroesSchema]` [bad-assignment]
+ ERROR tests/pandas/test_decorators.py:1158:29-56: `pandas.core.frame.DataFrame` is not assignable to `pandera.typing.pandas.DataFrame[OnlyZeroesSchema]` [bad-assignment]
+ ERROR tests/pandas/test_decorators.py:1159:29-56: `pandas.core.frame.DataFrame` is not assignable to `pandera.typing.pandas.DataFrame[OnlyZeroesSchema]` [bad-assignment]
+ ERROR tests/pandas/test_decorators.py:1176:27-54: `pandas.core.frame.DataFrame` is not assignable to `pandera.typing.pandas.DataFrame[OnlyZeroesSchema]` [bad-assignment]
+ ERROR tests/pandas/test_decorators.py:1177:25-52: `pandas.core.frame.DataFrame` is not assignable to `pandera.typing.pandas.DataFrame[OnlyZeroesSchema]` [bad-assignment]
+ ERROR tests/pandas/test_decorators.py:1203:23-50: `pandas.core.frame.DataFrame` is not assignable to `pandera.typing.pandas.DataFrame[OnlyOnesSchema] | pandera.typing.pandas.DataFrame[OnlyZeroesSchema]` [bad-assignment]
+ ERROR tests/pandas/test_decorators.py:1204:21-48: `pandas.core.frame.DataFrame` is not assignable to `pandera.typing.pandas.DataFrame[OnlyOnesSchema] | pandera.typing.pandas.DataFrame[OnlyZeroesSchema]` [bad-assignment]
+ ERROR tests/pandas/test_decorators.py:1211:27-54: `pandas.core.frame.DataFrame` is not assignable to `pandera.typing.pandas.DataFrame[OnlyOnesSchema] | pandera.typing.pandas.DataFrame[OnlyZeroesSchema]` [bad-assignment]
+ ERROR tests/pandas/test_decorators.py:1212:25-52: `pandas.core.frame.DataFrame` is not assignable to `pandera.typing.pandas.DataFrame[OnlyOnesSchema] | pandera.typing.pandas.DataFrame[OnlyZeroesSchema]` [bad-assignment]
+ ERROR tests/pandas/test_decorators.py:1219:27-54: `pandas.core.frame.DataFrame` is not assignable to `pandera.typing.pandas.DataFrame[OnlyOnesSchema] | pandera.typing.pandas.DataFrame[OnlyZeroesSchema]` [bad-assignment]
+ ERROR tests/pandas/test_decorators.py:1220:25-52: `pandas.core.frame.DataFrame` is not assignable to `pandera.typing.pandas.DataFrame[OnlyOnesSchema] | pandera.typing.pandas.DataFrame[OnlyZeroesSchema]` [bad-assignment]
+ ERROR tests/pandas/test_decorators.py:1245:27-54: `pandas.core.frame.DataFrame` is not assignable to `pandera.typing.pandas.DataFrame[OnlyOnesSchema] | pandera.typing.pandas.DataFrame[OnlyZeroesSchema]` [bad-assignment]
+ ERROR tests/pandas/test_decorators.py:1246:25-52: `pandas.core.frame.DataFrame` is not assignable to `pandera.typing.pandas.DataFrame[OnlyOnesSchema] | pandera.typing.pandas.DataFrame[OnlyZeroesSchema]` [bad-assignment]

dragonchain (https://github.com/dragonchain/dragonchain)
- ERROR dragonchain/webserver/lib/transactions.py:131:12-48: Returned type `dict[str, Unknown | None]` is not assignable to declared return type `dict[str, str]` [bad-return]
+ ERROR dragonchain/webserver/lib/transactions.py:131:31-47: `Unknown | None` is not assignable to `str` [bad-assignment]

static-frame (https://github.com/static-frame/static-frame)
+ ERROR static_frame/test/unit/test_frame.py:6736:35-63: No matching overload found for function `collections.defaultdict.__init__` called with arguments: (() -> None, dict[str, bool]) [no-matching-overload]

dd-trace-py (https://github.com/DataDog/dd-trace-py)
- ERROR ddtrace/contrib/internal/botocore/services/bedrock.py:293:12-58: Returned type `dict[str, list[str | Unknown | None] | list[Any] | list[Unknown]]` is not assignable to declared return type `dict[str, list[str]]` [bad-return]
+ ERROR ddtrace/contrib/internal/botocore/services/bedrock.py:293:21-25: `list[str | Unknown | None] | list[Any] | list[Unknown]` is not assignable to `list[str]` [bad-assignment]
+ ERROR ddtrace/contrib/internal/botocore/services/bedrock.py:293:44-57: `list[str | Unknown | None] | list[Unknown]` is not assignable to `list[str]` [bad-assignment]

pandas (https://github.com/pandas-dev/pandas)
+ ERROR pandas/io/stata.py:2530:38-46: `type[signedinteger[_32Bit]]` is not assignable to `type[int16] | type[int32] | type[int8]` [bad-assignment]
+ ERROR pandas/io/stata.py:2530:53-61: `type[signedinteger[_16Bit]]` is not assignable to `type[int16] | type[int32] | type[int8]` [bad-assignment]
+ ERROR pandas/io/stata.py:2530:68-75: `type[signedinteger[_8Bit]]` is not assignable to `type[int16] | type[int32] | type[int8]` [bad-assignment]

meson (https://github.com/mesonbuild/meson)
- ERROR mesonbuild/envconfig.py:98:68-120:2: `dict[str, list[str]]` is not assignable to `Mapping[str, ImmutableListProtocol[str]]` [bad-assignment]
- ERROR mesonbuild/envconfig.py:123:64-145:2: `dict[str, list[str]]` is not assignable to `Mapping[str, ImmutableListProtocol[str]]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:100:10-16: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
- ERROR mesonbuild/envconfig.py:151:71-156:2: `dict[str, list[str]]` is not assignable to `Mapping[str, ImmutableListProtocol[str]]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:101:12-19: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:102:11-18: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:103:15-25: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:104:10-16: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:105:16-22: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:106:13-21: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:107:15-25: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:108:13-22: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:109:13-22: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:110:13-21: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:113:13-22: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:114:15-25: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:115:13-22: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:116:19-28: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:117:16-27: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:118:18-31: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:119:16-28: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:125:11-17: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:126:11-17: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:127:11-17: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:128:11-17: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:129:16-27: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:130:16-27: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:131:15-25: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:132:16-27: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:133:13-21: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:134:16-27: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:135:14-23: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:136:16-33: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:139:14-23: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:140:14-23: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:141:19-33: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:142:13-21: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:143:16-27: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:144:20-35: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:152:13-21: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:153:19-27: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:154:16-27: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
+ ERROR mesonbuild/envconfig.py:155:18-31: `list[str]` is not assignable to `ImmutableListProtocol[str]` [bad-assignment]
- ERROR mesonbuild/interpreter/primitives/boolean.py:22:25-27:6: `dict[MesonOperator, tuple[type[bool], (obj: Unknown, x: Unknown) -> Unknown] | tuple[None, (obj: Unknown, x: Unknown) -> bool] | tuple[None, (obj: Unknown, x: Unknown) -> Unknown]]` is not assignable to attribute `TRIVIAL_OPERATORS` with type `dict[MesonOperator, tuple[tuple[type[Any], ...] | type[Any], (Unknown, Unknown) -> HoldableObject | MesonInterpreterObject | Sequence[TYPE_elementary] | Sequence[TYPE_var] | bool | dict[str, TYPE_elementary] | dict[str, TYPE_var] | int | str]]` [bad-assignment]
+ ERROR mesonbuild/interpreter/primitives/boolean.py:23:29-67: `tuple[None, (obj: Unknown, x: Unknown) -> Unknown]` is not assignable to `tuple[tuple[type[Any], ...] | type[Any], (Unknown, Unknown) -> HoldableObject | MesonInterpreterObject | Sequence[TYPE_elementary] | Sequence[TYPE_var] | bool | dict[str, TYPE_elementary] | dict[str, TYPE_var] | int | str]` [bad-assignment]
+ ERROR mesonbuild/interpreter/primitives/boolean.py:24:28-70: `tuple[None, (obj: Unknown, x: Unknown) -> bool]` is not assignable to `tuple[tuple[type[Any], ...] | type[Any], (Unknown, Unknown) -> HoldableObject | MesonInterpreterObject | Sequence[TYPE_elementary] | Sequence[TYPE_var] | bool | dict[str, TYPE_elementary] | dict[str, TYPE_var] | int | str]` [bad-assignment]
- ERROR mesonbuild/interpreter/primitives/integer.py:25:25-39:6: `dict[MesonOperator, tuple[type[int], (obj: Unknown, x: Unknown) -> Unknown] | tuple[None, (obj: Unknown, x: Unknown) -> Unknown]]` is not assignable to attribute `TRIVIAL_OPERATORS` with type `dict[MesonOperator, tuple[tuple[type[Any], ...] | type[Any], (Unknown, Unknown) -> HoldableObject | MesonInterpreterObject | Sequence[TYPE_elementary] | Sequence[TYPE_var] | bool | dict[str, TYPE_elementary] | dict[str, TYPE_var] | int | str]]` [bad-assignment]
+ ERROR mesonbuild/interpreter/primitives/integer.py:27:31-70: `tuple[None, (obj: Unknown, x: Unknown) -> Unknown]` is not assignable to `tuple[tuple[type[Any], ...] | type[Any], (Unknown, Unknown) -> HoldableObject | MesonInterpreterObject | Sequence[TYPE_elementary] | Sequence[TYPE_var] | bool | dict[str, TYPE_elementary] | dict[str, TYPE_var] | int | str]` [bad-assignment]
- ERROR mesonbuild/mintro.py:411:80-417:6: `dict[str, list[str] | str | None]` is not assignable to `dict[str, list[dict[str, str]] | list[str] | str]` [bad-assignment]
+ ERROR mesonbuild/mintro.py:412:20-45: `str | None` is not assignable to `list[dict[str, str]] | list[str] | str` [bad-assignment]
- ERROR run_project_tests.py:1360:52-75: Argument `dict[str, object | str]` is not assignable to parameter `attrib` with type `dict[str, str]` in function `xml.etree.ElementTree.SubElement` [bad-argument-type]
+ ERROR run_project_tests.py:1360:64-74: `object | str` is not assignable to `str` [bad-assignment]
- ERROR unittests/optiontests.py:328:45-74: Argument `dict[OptionKey, bool]` is not assignable to parameter `D_args` with type `dict[OptionKey, str | None]` in function `mesonbuild.options.OptionStore.set_from_configure_command` [bad-argument-type]
+ ERROR unittests/optiontests.py:328:69-73: `bool` is not assignable to `str | None` [bad-assignment]

core (https://github.com/home-assistant/core)
- ERROR homeassistant/components/bthome/logbook.py:32:16-35:10: Returned type `dict[str, DeviceEntry | str]` is not assignable to declared return type `dict[str, str]` [bad-return]
+ ERROR homeassistant/components/bthome/logbook.py:33:33-37: `DeviceEntry | str` is not assignable to `str` [bad-assignment]
- ERROR homeassistant/components/websocket_api/commands.py:1280:27-62: Cannot set item in `dict[str, dict[str, bool | None]]` [unsupported-operation]
+ ERROR homeassistant/components/websocket_api/commands.py:1280:53-61: `str` is not assignable to `bool | None` [bad-assignment]

xarray (https://github.com/pydata/xarray)
- ERROR xarray/coding/cftime_offsets.py:687:54-718:2: `dict[str, type[Day] | type[Hour] | type[Microsecond] | type[Millisecond] | type[Minute] | type[MonthBegin] | type[MonthEnd] | type[Second] | type[YearBegin] | type[YearEnd] | partial[QuarterBegin] | partial[QuarterEnd] | type[BaseCFTimeOffset]]` is not assignable to `Mapping[str, type[BaseCFTimeOffset]]` [bad-assignment]

kornia (https://github.com/kornia/kornia)
- ERROR kornia/models/efficient_vit/nn/act.py:31:51-37:2: `dict[str, partial[Unknown]]` is not assignable to `dict[str, type[Any]]` [bad-assignment]

pydantic (https://github.com/pydantic/pydantic)
+ ERROR pydantic/main.py:112:28-119: `(model: BaseModel, name: str, val: Any) -> dict[str, Any] | tuple[dict[str, Any], dict[str, Any] | None, set[str]] | Unknown` is not assignable to `(BaseModel, str, Any) -> None` [bad-assignment]

koda-validate (https://github.com/keithasaurus/koda-validate)
- ERROR koda_validate/serialization/json_schema.py:210:12-215:6: Returned type `dict[str, bool | dict[str, Serializable] | list[str] | str]` is not assignable to declared return type `dict[str, Serializable]` [bad-return]
- ERROR koda_validate/serialization/json_schema.py:230:12-235:6: Returned type `dict[str, bool | dict[str, Serializable] | list[str] | str]` is not assignable to declared return type `dict[str, Serializable]` [bad-return]
- ERROR koda_validate/serialization/json_schema.py:278:12-283:6: Returned type `dict[str, bool | dict[str, Serializable] | list[str] | str]` is not assignable to declared return type `dict[str, Serializable]` [bad-return]

graphql-core (https://github.com/graphql-python/graphql-core)
- ERROR src/graphql/utilities/build_client_schema.py:264:81-271:6: `dict[str, ((enum_introspection: IntrospectionEnumType) -> GraphQLEnumType) | ((input_object_introspection: IntrospectionInputObjectType) -> GraphQLInputObjectType) | ((interface_introspection: IntrospectionInterfaceType) -> GraphQLInterfaceType) | ((object_introspection: IntrospectionObjectType) -> GraphQLObjectType) | ((scalar_introspection: IntrospectionScalarType) -> GraphQLScalarType) | ((union_introspection: IntrospectionUnionType) -> GraphQLUnionType)]` is not assignable to `dict[str, (IntrospectionType) -> GraphQLNamedType]` [bad-assignment]
@github-actions

Copy link
Copy Markdown

Primer Diff Classification

❌ 1 regression(s) | ✅ 16 improvement(s) | ➖ 5 neutral | 22 project(s) total | +124, -57 errors

1 regression(s) across static-frame. error kinds: no-matching-overload. 16 improvement(s) across rotki, prefect, aiortc, materialize, apprise, mongo-python-driver, pandera, dragonchain, dd-trace-py, pandas, meson, xarray, kornia, pydantic, koda-validate, graphql-core.

Project Verdict Changes Error Kinds Root Cause
rotki ✅ Improvement +1, -3 New false positive in test file (dict literal inference) pyrefly/lib/alt/expr.rs
jax ➖ Neutral +25, -14 New bad-assignment errors on complex array values pyrefly/lib/alt/expr.rs
spark ➖ Neutral +2, -2 bad-argument-type, bad-assignment pyrefly/lib/alt/expr.rs
cwltool ➖ Neutral +5, -4 bad-assignment pyrefly/lib/alt/expr.rs
cryptography ➖ Neutral +1, -1 bad-argument-type, bad-assignment pyrefly/lib/alt/expr.rs
prefect ✅ Improvement +4, -2 New per-entry dict literal errors in images.py pyrefly/lib/alt/expr.rs
aiortc ✅ Improvement +2, -1 bad-assignment pyrefly/lib/alt/expr.rs
materialize ✅ Improvement +10 bad-assignment pyrefly/lib/alt/expr.rs
apprise ✅ Improvement +2, -2 bad-assignment pyrefly/lib/alt/expr.rs
mongo-python-driver ✅ Improvement -2 bad-assignment pyrefly/lib/alt/expr.rs
pandera ✅ Improvement +16, -8 bad-argument-type, bad-assignment pyrefly/lib/alt/expr.rs
dragonchain ✅ Improvement +1, -1 bad-assignment, bad-return pyrefly/lib/alt/expr.rs
static-frame ❌ Regression +1 no-matching-overload pyrefly/lib/alt/expr.rs
dd-trace-py ✅ Improvement +2, -1 bad-assignment, bad-return pyrefly/lib/alt/expr.rs
pandas ✅ Improvement +3 bad-assignment pyrefly/lib/alt/expr.rs
meson ✅ Improvement +46, -8 New per-value dict literal errors (list[str] vs ImmutableListProtocol[str]) pyrefly/lib/alt/expr.rs
core ➖ Neutral +2, -2 bad-assignment, bad-return pyrefly/lib/alt/expr.rs
xarray ✅ Improvement -1 bad-assignment pyrefly/lib/alt/expr.rs
kornia ✅ Improvement -1 bad-assignment pyrefly/lib/alt/expr.rs
pydantic ✅ Improvement +1 bad-assignment pyrefly/lib/alt/expr.rs
koda-validate ✅ Improvement -3 bad-return pyrefly/lib/alt/expr.rs
graphql-core ✅ Improvement -1 bad-assignment pyrefly/lib/alt/expr.rs
Detailed analysis

❌ Regression (1)

static-frame (+1)

The defaultdict(lambda: None, {'z': False}) call is valid Python. The PR's changes to dict literal inference with hints appear to have introduced a regression in overload resolution for defaultdict.__init__, where the type variable _VT should unify to bool | None but apparently fails to do so. Neither mypy nor pyright flag this.
Attribution: The changes to dict literal type inference in pyrefly/lib/alt/expr.rs — specifically the new infer_with_hint closure and the check_items logic that checks individual dict literal items against a contextual hint — likely changed how the dict literal {'z': False} is typed when passed as an argument to defaultdict.__init__. The new code path may be causing the dict literal's type to be resolved differently during overload matching, leading to a failure to match any overload of defaultdict.__init__.

✅ Improvement (16)

rotki (+1, -3)

New false positive in test file (dict literal inference): The new error at test_history_base_entry.py:1433 incorrectly flags 5 (int) as not assignable to str in a dict literal {'identifier': 5, 'amount': '0.0012', 'asset': ...}. This dict has mixed value types (int and str), so the value type should be inferred as str | int, not str. The new per-item checking logic appears to be comparing against only part of the expected type. This is pyrefly-only (neither mypy nor pyright flag it).
Removed false positives in aggregator.py (bound method self parameter): The 3 removed errors were false positives where bound methods with self: Self@ChainsAggregator were being compared against callable types without self. The source code has # type:ignore comments confirming these were known false positives. The PR's change to per-item dict checking apparently no longer triggers these whole-dict-level errors, which is correct behavior.

Overall: The PR narrows dict literal assignment errors to point at the specific mismatching key/value. This is generally a good change. The 3 removed errors in aggregator.py were false positives (bound method self parameter mismatch, confirmed by # type:ignore comments in source). However, the new error at test_history_base_entry.py:1433 is a false positive — entry is a dict with heterogeneous value types (strings, ints, lists, etc.), and the 5 being assigned as an 'identifier' value is perfectly valid. The dict was never constrained to dict[str, str]. Looking more carefully at the code: entry starts as entries[0].copy() where entries[0] is a dict literal with mixed value types. Then entry['identifiers'] = [1, 2, 3, 8, 5] assigns a list of ints. Then entry['fee'] = [{'identifier': 5, ...}] — the inner dict has int value for 'identifier' key. Pyrefly seems to be incorrectly inferring the inner dict's type as dict[str, str] and then flagging the 5 as not assignable to str. This is a type inference error — the inner dict literal {'identifier': 5, 'amount': '0.0012', 'asset': A_WETH.identifier} should be inferred as dict[str, str | int] or dict[str, Any], not dict[str, str]. The net effect is: 3 false positives removed (good), 1 false positive added (bad). Overall slightly positive but mixed.

Attribution: The PR changed dict literal error checking in pyrefly/lib/alt/expr.rs to narrow errors to the first mismatching key/value item. The new check_items logic with infer_with_hint and has_type_mismatch now reports per-item errors instead of whole-dict errors. This caused: (1) the 3 removed errors in aggregator.py — the old code reported the whole dict literal as mismatching, but the new per-item checking apparently doesn't trigger for these bound-method cases (the self parameter mismatch is no longer surfaced); (2) the new error at test_history_base_entry.py:1433 — the new per-item checking is now flagging 5 (an int) as not assignable to str, which appears to be a false positive since entry is a dict[str, Any] (or at least has mixed value types).

prefect (+4, -2)

New per-entry dict literal errors in images.py: 4 new errors correctly flag individual dict values (str, str | None, str | None, bool) that don't match the expected Dict[str, Any] value type. These replace 1 broad whole-dict error. All 4 confirmed by pyright. This is an improvement in error precision — same real bugs, better error locations.
Removed whole-dict error in images.py: The old error dict[str, bool | dict[str, Any] | str | None] is not assignable to dict[str, dict[str, Any]] spanning lines 64-70 was replaced by the 4 per-entry errors above. This is an improvement — the error wasn't lost, it was refined.
Removed error in jinja_filters.py: The dict all_filters includes flow_run_id which has type (str | None) -> str | None, not matching the declared value type Callable[[Mapping[str, Any], Any], str | None]. The code even has a # type: ignore comment acknowledging this mismatch. This was a real type error that pyrefly used to catch but no longer does — a regression.

Overall: This is a mixed change. In images.py, the old single broad error spanning lines 64-70 is replaced by 4 specific errors pointing at each mismatching value (lines 65, 66, 67, and presumably 68). All 4 are confirmed by pyright. This is better error reporting — same bugs caught, but with more precise locations. However, in jinja_filters.py, a real type error (flow_run_id has type (str | None) -> str | None which doesn't match Callable[[Mapping[str, Any], Any], str | None]) is no longer reported. This is a genuine regression — a real bug is no longer caught. Net: +4 new correct errors, -1 replaced error (improvement), -1 lost real error (regression). The images.py changes are clearly an improvement in error quality. The jinja_filters.py removal is a regression. Overall, the improvements outweigh the regression (better error locations for real bugs, with one edge case regression).

Attribution: The changes in pyrefly/lib/alt/expr.rs are responsible. The new check_items logic and infer_with_hint closure check individual dict literal entries against the hint type and report per-entry errors. The has_type_mismatch flag combined with the early return at the end (lines 1539-1546 in the new code) causes the function to return the hint type directly when any mismatch is found, which suppresses the old whole-dict-level error. For images.py, this correctly replaces 1 broad error with 4 specific errors (net +3). For jinja_filters.py, the removal of the error appears to be a bug in the new logic — possibly because the dict entries that match the hint cause the overall check to pass, while the mismatching entry (flow_run_id) doesn't trigger the per-item check for some reason (perhaps related to how check_items is conditioned on hint.types().len() == 1).

aiortc (+2, -1)

This is a clear improvement in error reporting quality. The same real type bug (inserting None values into a dict[str, int | str]) is still caught, but now with more precise error locations. Instead of one error spanning the entire dict literal (lines 44-49), pyrefly now emits two errors pointing directly at obj.sdpMid (line 46, type str | None) and obj.sdpMLineIndex (line 47, type int | None), both of which are not assignable to the declared value type int | str. The net error count goes from 1 to 2, but both new errors are correct and more actionable than the single old error. Pyright confirms both new errors.
Attribution: The changes in pyrefly/lib/alt/expr.rs narrowed dict-literal assignment errors to point at the first mismatching key/value instead of the whole {...} expression. The new DictLiteralItem variant in pyrefly/lib/error/context.rs and pyrefly/lib/error/display.rs provides the per-item error messages. This causes the single broad error on lines 44-49 to be replaced by two specific errors on lines 46 and 47.

materialize (+10)

The PR's new dict literal item-level checking introduces false positive bad-assignment errors. The networks parameter at each flagged location receives a value like {"balancerd": {"ipv4_address": "..."}} — a dict[str, dict[str, str]]. The error message claims str is not assignable to list[str], which suggests the networks parameter in the Service base class (or its subclasses) may be typed as list[str] (a simple list of network names), while the actual usage passes a dict with network configuration details. The PR's new per-item checking logic is likely comparing the provided dict value types against the declared list[str] parameter type and producing a type mismatch. Regardless of the exact mechanism, since neither mypy nor pyright flag any of these 10 locations, these are clearly false positives introduced by the PR's new checking logic.
Attribution: The changes in pyrefly/lib/alt/expr.rs that add per-item dict literal checking (check_items, has_type_mismatch, the new infer_with_hint closure) are incorrectly identifying type mismatches in nested dict literals. The new DictLiteralItem variant in pyrefly/lib/error/context.rs produces bad-assignment errors pointing at individual values. The logic appears to be comparing the inner dict's value type (str) against the outer dict's expected value type (possibly list[str]), producing spurious errors.

apprise (+2, -2)

This is a net improvement in error reporting quality. The PR narrows dict literal assignment errors to point at the specific mismatching key or value instead of the entire dict literal.

fluxer.py:789 — The new error says NotifyFormat | OverflowMode | bool | float | str | None is not assignable to str for self.mode. Looking at the code, self.mode is assigned on line 310-314 as either mode.strip().lower() (which is str) or self.template_args["mode"]["default"] (which is FluxerMode.CLOUD = "cloud", also str). However, self.template_args is built from dict(NotifyBase.template_args, **{...}) and the values include dicts with mixed types ("default": False, "default": True, "min": 0, etc.). Pyrefly appears to be inferring self.mode through the broader template_args dict value type rather than narrowing through the specific assignment. The inferred type NotifyFormat | OverflowMode | bool | float | str | None is overly broad — self.mode is always str at this point. This is a pyrefly-only false positive (neither mypy nor pyright flag it). However, the old error was also a false positive (same underlying issue, just wider span). So this is a wash — one false positive replaced another.

test_attach_http.py:148getsize(path) returns int, but the dict is annotated ClassVar[dict[str, str]]. This is a genuine type error (confirmed by pyright). The new error correctly points to getsize(path) specifically rather than the whole dict. This is strictly better error reporting.

Overall: 2 removed errors (old-style whole-dict errors) replaced by 2 new errors (precise per-entry errors). One is a genuine improvement (test_attach_http.py — real bug, better location). The other (fluxer.py) is a false positive in both old and new forms, but the new form at least points to the specific entry. Net effect is improved error quality.

Attribution: The changes in pyrefly/lib/alt/expr.rs implement per-item checking of dict literals against a contextual hint. Instead of only checking the whole inferred dict type against the hint, the new code checks each key and value individually via infer_with_hint and check_type, emitting errors at the specific key/value range. The new TypeCheckKind::DictLiteralItem variant in pyrefly/lib/error/context.rs and pyrefly/lib/error/display.rs provides the error message format. This causes the error span to narrow from the whole {...} to the specific offending entry.

mongo-python-driver (-2)

The removed errors were whole-dict-literal type mismatch errors reported as bad-assignment. The PR likely changes dict literal type checking to report errors on individual key-value pairs rather than on the entire dict literal. Looking at the error messages, there are multiple sources of type incompatibility in the dict: (1) _authenticate_oidc has a different signature (3 parameters including reauthenticate: bool, and in the sync case returns Mapping[str, Any] | None instead of None), and (2) functools.partial objects are typed as partial[T] rather than as Callable[..., T], which many type checkers don't consider assignable to Callable. With per-item error reporting, the _authenticate_oidc entry already has a # type:ignore[dict-item] comment that would suppress its individual error. For the functools.partial entries, the PR may have also improved how partial types are checked against Callable[..., ...] targets, or the per-item checking may handle them differently than the whole-dict union approach. The old behavior computed a union of all value types and checked the entire union against the target type, causing a single error on the whole dict. The new per-item approach checks each value individually, which is more precise — entries that individually match pass, and only genuinely mismatching entries are flagged. This is an improvement in error reporting precision.
Attribution: The changes in pyrefly/lib/alt/expr.rs are responsible. The new logic in the dict literal handling code checks individual key/value pairs against the hint type and sets has_type_mismatch. When a mismatch is found, it reports the error on the specific mismatching item's range rather than the whole dict. The early return at the end (if has_type_mismatch ... return hint type) means the overall dict type becomes the hint type, preventing the downstream whole-dict assignment error. Since the _authenticate_oidc entry has # type:ignore[dict-item], the per-item error is suppressed, and the functools.partial entries may now pass the per-item check, resulting in no errors being reported at all.

pandera (+16, -8)

This is a neutral-to-slightly-worse change in error reporting for this project. The underlying type issue is identical: pd.DataFrame is not pandera.typing.DataFrame[Schema]. Before: 8 errors reported at the dict-literal/argument level. After: 16 errors reported at individual value entries within those dicts. The error count doubled because each dict has 2 entries and each entry now gets its own error. The error kind changed from bad-argument-type to bad-assignment, which is slightly misleading since these are function arguments, not assignments. However, the PR's intent (narrowing error spans to the offending entry) is reasonable for the general case. The net effect is more errors (16 vs 8) for the same underlying issues, with more precise locations but a less accurate error kind. Since the code already has # type: ignore [arg-type] comments and the errors are all pyrefly-only, this is essentially a lateral move in error reporting quality — the errors were false positives before (pd.DataFrame IS compatible with pandera.typing.DataFrame at runtime via the decorator) and remain false positives now, just with different locations and counts.
Attribution: The changes in pyrefly/lib/alt/expr.rs narrowed dict-literal assignment errors to point at the first mismatching key/value entry instead of the whole {...} literal. This created a new TypeCheckKind::DictLiteralItem in pyrefly/lib/error/context.rs that maps to ErrorKind::BadAssignment. Previously, the error was reported as bad-argument-type on the whole dict literal passed to the function; now it's reported as bad-assignment on each individual value entry. The net effect is: 8 old bad-argument-type errors on the whole dict → 16 new bad-assignment errors on individual values (2 values per dict × 8 call sites).

dragonchain (+1, -1)

This is a clear improvement in error reporting precision. The same type error is being caught (the value txn_model.txn_id of type Unknown | None doesn't match the expected str value type of Dict[str, str]), but the error now points to the specific problematic value (txn_model.txn_id at column 31-47) rather than the entire return expression (columns 12-48). The error kind changed from bad-return to bad-assignment because the check is now done at the dict-entry level rather than the return-statement level. This matches pyright's behavior (which also flags this). The PR's test cases confirm this is intentional — all dict literal error messages were updated to point at the specific mismatching key or value.
Attribution: The change in pyrefly/lib/alt/expr.rs in the dict literal inference logic now checks individual key/value pairs against the dict type hint and reports errors on the specific mismatching entry rather than on the whole dict literal. The new TypeCheckKind::DictLiteralItem variant in pyrefly/lib/error/context.rs produces bad-assignment errors instead of bad-return errors. When a mismatch is found (has_type_mismatch = true), the code returns the hint type directly and skips the outer return-type check, which is why the bad-return error disappears and is replaced by the more targeted bad-assignment error.

dd-trace-py (+2, -1)

This is a pure error reporting improvement. The same real type issue (values of type list[str | Unknown | None] don't match declared list[str]) was previously reported as one bad-return error spanning the entire dict literal {"text": text, "finish_reason": finish_reason}. Now it's reported as two bad-assignment errors pointing precisely at text (col 21-25) and finish_reason (col 44-57). The net error count goes from 1 to 2, but this is because there are genuinely two mismatching values. The errors are confirmed by pyright. The underlying code has a real type issue: the function _extract_text_and_response_reason declares -> dict[str, list[str]] but text can contain None values (from .get() calls without defaults) and finish_reason similarly.
Attribution: The change in pyrefly/lib/alt/expr.rs adds per-key/value type checking for dict literals against a contextual hint. When a dict literal has a type hint, instead of reporting a single bad-return error on the entire {...} expression, it now reports individual bad-assignment errors on each mismatching key or value. The new TypeCheckKind::DictLiteralItem variant in pyrefly/lib/error/context.rs and pyrefly/lib/error/display.rs provides the error message format. The has_type_mismatch flag causes the dict expression to return the hint type directly (suppressing the old whole-dict error), while the individual item errors are emitted.

pandas (+3)

These are false positives. np.int32 is defined in numpy's stubs as numpy.int32 = numpy.signedinteger[numpy._typing._32Bit]. So type[numpy.int32] and type[signedinteger[_32Bit]] should be the same type. Pyrefly is failing to recognize this equivalence when checking individual dict values against the inferred dict value type hint. Neither mypy nor pyright flags these. The PR's change to per-value dict literal checking exposed a pre-existing type resolution issue with numpy type aliases.
Attribution: The PR changed dict literal checking in pyrefly/lib/alt/expr.rs to narrow error reporting to individual dict values instead of the whole dict literal. The new check_items logic with infer_with_hint and check_type calls now checks each value individually against the value hint. Previously, the whole dict type was checked against the hint type. The narrower per-value checking is now surfacing false positives for numpy types where type[signedinteger[_32Bit]] is not recognized as assignable to type[int32] (which is the same type). The old code would have produced a single error on the whole dict literal, but the type inference likely resolved the dict type correctly as dict[int, type[int16] | type[int32] | type[int8]] which matched the hint. Now with per-value checking, each value is individually checked and the type resolution for np.int32 as type[signedinteger[_32Bit]] doesn't match type[int32] in pyrefly's type system.

meson (+46, -8)

New per-value dict literal errors (list[str] vs ImmutableListProtocol[str]): 46 new errors flagging each individual value in dict literals like 'c': ['CC'] where list[str] doesn't satisfy ImmutableListProtocol[str]. These are the same false positives that existed before but now multiplied per-entry instead of per-dict. 0/46 co-reported by mypy/pyright. This is a regression — more noise for the same non-issue.
Removed whole-dict errors: 6 removed errors that previously spanned the entire dict literal. These were false positives being replaced by the per-value errors above. The removal itself is neutral since the errors are replaced, not fixed.
Removed bad-argument-type errors: 2 removed errors about dict type mismatches in function arguments. These appear to be genuine false positive fixes from improved dict type inference. This is an improvement.

Overall: The PR replaces 6 whole-dict errors with 46 per-value errors for the same underlying issue (list[str] vs ImmutableListProtocol[str]). The underlying error was already a false positive not reported by mypy/pyright (0/46 cross-check). The PR made the error reporting worse by multiplying the error count ~7x. While the 2 removed bad-argument-type errors are a small improvement, the net effect is 38 more false positive errors. This is a regression in error reporting quality.

Attribution: The changes in pyrefly/lib/alt/expr.rs (the infer_with_hint closure and the per-item check_type calls) cause errors to be emitted per dict entry instead of per whole dict literal. The has_type_mismatch flag and early return at line 1539-1548 should suppress the whole-dict error, but the per-item errors multiply the count. The new TypeCheckKind::DictLiteralItem in pyrefly/lib/error/context.rs and pyrefly/lib/error/display.rs provides the error formatting.

xarray (-1)

The PR improved error reporting for dict literals by narrowing the error span from the entire dict to the specific mismatching entry. In xarray's case, the _FREQUENCIES dict has partial(QuarterEnd, month=12) etc. as values that don't match type[BaseCFTimeOffset]. Previously pyrefly reported one big error spanning the whole dict literal. Now it reports errors on the individual partial(...) values — and since xarray already has # type: ignore[dict-item] comments on those exact lines (693-695), the narrowed errors are properly suppressed. This is an improvement in error reporting quality: the error is more precise and respects existing type-ignore comments correctly.
Attribution: The change in pyrefly/lib/alt/expr.rs modified dict literal type checking to report errors on individual mismatching key/value entries rather than on the entire dict literal. When a dict hint is present and a mismatch is found, the error now points to the specific offending entry (via check_type with x.value.range() or key.range()). The TypeCheckKind::DictLiteralItem variant added in pyrefly/lib/error/context.rs supports this narrower error reporting. For xarray, this means the error that previously spanned the whole dict (lines 687-718) is now narrowed to the specific partial(...) values, which are covered by existing # type: ignore[dict-item] comments, effectively suppressing the errors.

kornia (-1)

This is an improvement in error reporting precision. The PR narrows dict literal assignment errors to point at the first mismatching key/value entry instead of the entire dict literal. The underlying type error is that partial(nn.GELU, approximate="tanh") produces a partial[Unknown] value which is not assignable to type[Any] (the value type of the annotated dict). Previously, the error spanned the entire dict literal (lines 31-37) and reported the full dict type mismatch (dict[str, partial[Unknown]] is not assignable to dict[str, type[Any]]). With the PR's changes, the error would instead point specifically at the offending entry (line 36, the partial(nn.GELU, approximate="tanh") value) and report a more specific error like partial[Unknown] is not assignable to type[Any]. The test cases in the PR diff confirm this pattern: errors like dict[str, str] is not assignable to dict[str, int] become str is not assignable to int pointing at the specific value. This makes errors more actionable for developers by identifying exactly which entry in the dict literal is causing the type mismatch.
Attribution: The change in pyrefly/lib/alt/expr.rs modified dict literal type checking to report errors on individual mismatching entries rather than the whole dict expression. The new DictLiteralItem variant in pyrefly/lib/error/context.rs and the has_type_mismatch logic in expr.rs cause the error to be emitted at the specific value that doesn't match, and then the overall dict expression returns the hint type directly (avoiding the duplicate whole-dict error). This is why the broad-span error on lines 31-37 was removed — it's replaced by a narrower error on the specific partial(...) value.

pydantic (+1)

This is a real type error that pyright also catches (suppressed via # pyright: ignore[reportAssignmentType]). The lambda returns dict[str, Any] | tuple[...] | Unknown but the dict value type requires Callable[[BaseModel, str, Any], None]. The PR's narrowed dict literal error reporting now correctly identifies this specific mismatching entry. The Unknown component likely comes from PluggableSchemaValidator type resolution, but even without it, the SchemaValidator.validate_assignment return type is non-None. This is an improvement — more precise error reporting catching a genuine type-level issue.
Attribution: The change to AnswersSolver in pyrefly/lib/alt/expr.rs introduced per-item dict literal checking. Previously, the whole dict literal was checked as a unit. Now individual key/value entries are checked against the hint type, and the first mismatch is reported with a narrowed range. This caused the lambda value at line 112 to be individually checked against Callable[[BaseModel, str, Any], None], revealing the return type mismatch that was previously absorbed into the whole-dict check.

koda-validate (-3)

The removed errors were false positives. The dict literals contain values of types str, bool, list[str], and dict[str, Serializable], which are all subtypes of Serializable (typically defined as a recursive type alias like Union[None, bool, int, float, str, list['Serializable'], dict[str, 'Serializable']]). The old checker inferred the overall value type of the dict literal as the union bool | dict[str, Serializable] | list[str] | str and then failed to recognize that dict[str, bool | dict[str, Serializable] | list[str] | str] is assignable to dict[str, Serializable], even though every member of that union is individually a subtype of Serializable. The new per-item checking correctly validates each entry against Serializable and produces no spurious error.
Attribution: The change to dict literal inference in pyrefly/lib/alt/expr.rs (the infer_with_hint closure and per-item check_type calls) now checks individual dict entries against the hint type rather than computing a widened overall type. When all items match, the has_type_mismatch flag stays false, and the dict is correctly typed as dict[str, Serializable] instead of the overly-specific dict[str, bool | dict[str, Serializable] | list[str] | str].

graphql-core (-1)

The PR improved error reporting for dict literals by narrowing the error range from the entire dict literal to the specific mismatching key or value. In this graphql-core project, the dict at line 264 has values with more specific callable signatures than the declared type Callable[[IntrospectionType], GraphQLNamedType]. Each value function accepts a more specific subtype of IntrospectionType (e.g., IntrospectionScalarType, IntrospectionObjectType, etc.) and returns a more specific subtype of GraphQLNamedType. Due to callable contravariance in parameter types, these are not assignable to Callable[[IntrospectionType], GraphQLNamedType]. Previously, pyrefly reported one error spanning the whole dict (lines 264-271). Now it reports individual errors on each value, and those individual errors are suppressed by the existing # type: ignore comments on lines 265-270. The net effect is the removal of a broad, less-actionable error in favor of precise, per-item errors that happen to be suppressed. This is an improvement in error reporting quality — the error was real but the reporting was too coarse-grained.
Attribution: The change in pyrefly/lib/alt/expr.rs modified dict literal type checking to report errors on individual mismatching key/value items rather than on the entire dict literal. When a type mismatch is found in a dict item, has_type_mismatch is set to true, and the function returns the hint type directly (lines at the end of the diff). This means the overall dict type matches the hint, so no dict-level error is emitted. Instead, individual item-level errors are emitted at the specific key/value ranges. In this project's case, the # type: ignore comments on each value line suppress those individual errors.

➖ Neutral (5)

jax (+25, -14)

New bad-assignment errors on complex array values: These 25 new errors point to specific complex-typed numpy array values (complex64/complex128) in dict literals where the inferred dict value type only includes floating-point array types. The errors are true positives — the complex arrays genuinely don't match the floating-point type. However, 22/25 are pyrefly-only, and the increase from 14 to 25 total errors for the same underlying issue means more noise. The errors are correct but more verbose than before.
Removed unsupported-operation errors on dict setitem: These 14 removed errors were reporting the same underlying type mismatch at the dict-setitem level ('Cannot set item in dict[...]'). They were true positives being replaced by the more granular bad-assignment errors above. This is not a loss of error detection — it's a change in error reporting location.

Overall: This is a neutral-to-slightly-mixed change. The same underlying type errors are being detected (complex arrays don't match floating-point array types in the inferred dict value type). The PR changed error reporting from whole-dict-level unsupported-operation errors to per-value bad-assignment errors. Both the old and new errors are detecting real type mismatches — data_2025_10_20 is initialized as {} and its type is inferred from the first entries (f32/f64 which use floating), then complex entries don't match. The new errors are arguably better-located (pointing to the specific mismatching value) but more numerous (25 vs 14). The underlying type issue is the same, and both old and new errors are true positives for the same root cause. The net effect is a change in error reporting style with a slight increase in error count, which is neutral overall.

Attribution: The changes in pyrefly/lib/alt/expr.rs modified dict literal type checking to report errors on individual mismatching key/value entries rather than on the whole dict expression. The new DictLiteralItem variant in pyrefly/lib/error/context.rs and the has_type_mismatch tracking in expr.rs cause pyrefly to emit bad-assignment errors pointing at specific values (like the complex array tuple on line 119) instead of unsupported-operation errors on the dict setitem operation. The increase from 14 to 25 errors happens because each dict with complex data has multiple mismatching values (inputs and expected_outputs), and the new code reports each one separately.

spark (+2, -2)

This is a net-neutral change in terms of error count (2 removed, 2 added), but the nature of the errors is essentially the same false positive with different presentation. The code at line 28-33 does datas = []; datas.append({...all int lists...}); ...; datas.append({...mixed str/int lists...}). Pyrefly infers datas as list[dict[str, list[int]]] from the first append, then flags the fourth append where key "a" has value ["a", "a", "c"] (a list[str]). Similarly at line 67-69, datas is re-assigned to [], the first append has all list[str] values, and the second append has "b": [4, 0, 1] (a list[int]). Neither mypy nor pyright flags these. The old errors were false positives (wrong about the whole dict), and the new errors are also false positives (wrong about individual values). The PR improved error presentation (narrower spans) but didn't fix the underlying false positive. Since the old errors were false positives being removed (improvement) and the new errors are also false positives being added (regression), these cancel out. However, the new errors have better error spans pointing to the actual mismatch location, which is a slight improvement in error quality even though both old and new are false positives. On balance, this is neutral — same number of false positives, slightly better error messages.
Attribution: The PR changes in pyrefly/lib/alt/expr.rs narrowed dict literal error reporting to point at the specific mismatching key/value instead of the whole dict literal. The new DictLiteralItem variant in TypeCheckKind (in pyrefly/lib/error/context.rs) emits bad-assignment errors on individual values. Previously, the error was reported as bad-argument-type on the whole list.append(...) call. Now, pyrefly reports bad-assignment on the individual dict value that doesn't match the inferred hint. The error kind changed from bad-argument-type to bad-assignment, and the span narrowed from the whole dict to the specific value. But the underlying issue remains: pyrefly infers datas too narrowly from the first append and then flags subsequent appends with different value types.

cwltool (+5, -4)

This is a neutral-to-improvement change in error reporting quality. The PR narrows dict-literal assignment errors to point at the first mismatching key/value entry instead of the whole {...} expression. The 4 removed errors and 5 new errors represent the same underlying type mismatches, just reported at different granularity. The net effect is +1 error, which appears to be because the narrower reporting sometimes splits what was one dict-level error into separate key and value errors. The underlying type issues being flagged are the same — complex CWL type mismatches that neither mypy nor pyright flag (likely due to incomplete stubs for cwl_utils types). The code at tests/test_examples.py:79-89 constructs a CWLParameterContext dict where the value for "foo" is a nested dict (which pyrefly infers as a complex type) and "lst" is ["A", "B"] (a list[str]), and the CWLParameterContext TypedDict expects CWLOutputType values. These are pre-existing type mismatches that were already being reported — just at a different location now. Similarly for command_line_tool.py:850 where cmdline (the result of flatten(), inferred as a list type) is assigned as a value in a dict typed as dict[str, MutableSequence[str | int] | CWLObjectType]. The flatten function's return type and the concatenation with ["docker", "run", dockerimg] produce a list whose element types may not exactly match MutableSequence[str | int], leading to the type error.
Attribution: The changes in pyrefly/lib/alt/expr.rs implement per-item type checking for dict literals against a contextual hint. When a dict literal has a type hint, instead of reporting one error on the whole dict expression, it now reports errors on individual mismatching keys/values. The TypeCheckKind::DictLiteralItem variant added in pyrefly/lib/error/context.rs and pyrefly/lib/error/display.rs provides the error formatting. This causes the error span to shift from the whole dict to the specific mismatching entry, which is why we see removals of dict-level errors and additions of value-level errors.

cryptography (+1, -1)

This is a mixed change. The removed error was a false positive — the function load_fips_dsa_sig_vectors returns list[dict] (line 384), which is list[dict[Any, Any]]. There should be no constraint on what types the dict values can be. The old error incorrectly constrained the dict to dict[str, int | str] based on inference from the first dict literal appended to vectors at line 406-408, which contains {"p": int(value, 16), "digest_algorithm": digest_algorithm}, giving pyrefly an inferred element type of dict[str, int | str].

The new error (bytes is not assignable to int | str) is also a false positive for the same reason — the return type is list[dict], so there's no type hint requiring values to be int | str. Pyrefly is inferring a type for vectors from the first append call (line 406-408) and then checking later entries against that inferred type, but since the declared return type is list[dict] (unparameterized), this check is spurious. The specific value flagged is binascii.unhexlify(hexmsg) which returns bytes, and pyrefly complains that bytes doesn't match the inferred value type int | str.

The net effect is neutral-to-slightly-improved: the old error was a false positive on the whole dict literal passed to append (broad, confusing), and the new error is a false positive on a specific value within the dict (narrower, but still wrong). The count went from 1 false positive to 1 false positive, with better error location. Neither mypy nor pyright flags either error.

Since both the old and new errors are false positives of roughly equal severity (one removed, one added), this is essentially neutral — the error quality improved (more precise location) but the false positive wasn't eliminated.

Attribution: The changes in pyrefly/lib/alt/expr.rs narrowed dict-literal assignment errors to point at the first mismatching key/value instead of the whole {...} expression. The new DictLiteralItem variant in pyrefly/lib/error/context.rs and display.rs produces the bad-assignment error with the message format "X is not assignable to Y". This caused the old broad bad-argument-type error on the whole dict to be replaced by a narrower bad-assignment error on the specific value (binascii.unhexlify(hexmsg) which is bytes).

core (+2, -2)

This is a neutral-to-slightly-improved change in error reporting granularity. The PR narrows dict literal errors to point at the specific mismatching value rather than the whole dict. Both files have the same number of errors before and after (1 each). The errors detect the same underlying issues.

For logbook.py: The old error was bad-return on the whole dict (lines 32-35), the new error is bad-assignment on name (line 33). The underlying issue is that name has type DeviceEntry | str due to how pyrefly infers (device and device.name) or f"...". This is arguably a real type issue (the and expression could produce a DeviceEntry if device is truthy but device.name is falsy/None — though in practice device.name is str | None so the or fallback handles it). Neither mypy nor pyright flag this.

For commands.py: The old error was unsupported-operation on the whole line saying "Cannot set item in dict[str, dict[str, bool | None]]", the new error is bad-assignment claiming str is not assignable to bool | None. This appears to be a false positive. The variable result is declared as result = {} with no type annotation (line 1260). Pyrefly infers the type of result as dict[str, dict[str, bool | None]] — a nested dict where the inner dict maps str keys to bool | None values. This inference appears to come from the else branch (line 1282: {"valid": True, "error": None} → inner dict type dict[str, bool | None]). The except branch (line 1280: {"valid": False, "error": str(err)}) then conflicts because str(err) produces a str, which is not assignable to bool | None. In reality, the inner dict values should be bool | str | None to accommodate both branches. Neither mypy nor pyright flag this.

The net effect is zero change in error count (2 removed, 2 added), with narrower error spans. The underlying issues being flagged are the same, and both are pyrefly-only. The error messages are more precise (pointing to the exact problematic value), which is the stated goal of the PR.

Attribution: The changes in pyrefly/lib/alt/expr.rs narrowed dict-literal assignment errors to point at the first mismatching key/value instead of the whole {...} expression. The new TypeCheckKind::DictLiteralItem variant in pyrefly/lib/error/context.rs and pyrefly/lib/error/display.rs produces bad-assignment errors with the format "X is not assignable to Y" instead of the old whole-dict error. This is why the error kind changed from bad-return/unsupported-operation to bad-assignment, and the span narrowed to the specific offending value.

Suggested fixes

Summary: The new dict literal per-item checking logic interferes with overload resolution by prematurely returning the hint type when a mismatch is detected during tentative overload matching, causing defaultdict.init to fail.

1. In the dict literal inference code in AnswersSolver in pyrefly/lib/alt/expr.rs, the check_items guard should also verify that the errors collector is not a tentative/overload-resolution collector. Specifically, the check_items condition hint.filter(|hint| hint.errors().[is_some()](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/alt/expr.rs)).is_some_and(|hint| hint.types().len() == 1) should be further restricted to not activate during overload resolution. One approach: when has_type_mismatch is true and the code is about to return the hint type early (lines 1539-1548), check whether the errors were actually emitted (i.e., not suppressed by a tentative error collector). Alternatively, the check_items logic should not set has_type_mismatch = true when the check_type call doesn't actually emit an error (e.g., because the error collector is in tentative/suppression mode). The simplest fix: track whether check_type actually produced an error before setting has_type_mismatch = true, or gate check_items on the error collector not being in tentative mode (used during overload resolution).

Files: pyrefly/lib/alt/expr.rs
Confidence: high
Affected projects: static-frame
Fixes: bad-argument-type
The defaultdict(lambda: None, {'z': False}) call uses overload resolution. During overload matching, each overload is tried tentatively. The new per-item dict checking fires during this tentative phase, detects a mismatch against one overload's expected type, sets has_type_mismatch = true, and then returns the hint type early (lines 1539-1548). This changes the inferred type of the dict literal from dict[str, bool] to whatever the hint type was for that overload attempt, which then causes the correct overload to also fail. The fix should prevent the early return of the hint type when in a tentative/overload-resolution context, or should not activate per-item checking during overload resolution at all. This would eliminate the 1 pyrefly-only error in static-frame.


Was this helpful? React with 👍 or 👎

Classification by primer-classifier (22 LLM)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

3 participants