Each error ends with a code in brackets. This page gives the cause and the fix for each code. The examples show the real messages.
Some errors are unproven results, not faults: the checker cannot prove that the code is correct. The setting allow_unproven decides which of them it reports, and the note under such an error names the level that lets it pass. See Configuration.
To silence one line, add # type: ignore[code]. To turn a code off, use the disable_error_code setting of mypy.
A field of a schema is not a data type.
schemas.pymypy: 2 errors
1import polars as pl
2
3from astrolabe import Columns, TypedDataFrame
4
5
6class Prices(Columns):
7 symbol: pl.String
8 price: float
↑error: Column "price" must be a polars dtype or an astrolabe dtype marker [columns-field-type]
9
10
11class Partial(Columns, total=False):
↑error: Columns subclass "Partial" must not use total=False, Required[...], or NotRequired[...]: every column is required [columns-not-total]
12 symbol: pl.String
13
14
15prices: TypedDataFrame[Prices]
16partial: TypedDataFrame[Partial]
Cause. A field of a Columns class has a type that is not a Polars data type or a marker of one, for example float.
Fix. Give the field a Polars data type, for example pl.Float64. For a data type with parameters, use its marker. See Data types.
A schema declares closed=False.
schemas.pymypy: 1 error
1import polars as pl
2
3from astrolabe import Columns, TypedDataFrame
4
5
6class Loose(Columns, closed=False):
↑error: Columns subclass "Loose" must not use closed=False. Declare a remainder with other_columns() and write it as the frame's second argument, for example TypedDataFrame[Loose, Rest] [columns-not-closed]
7 symbol: pl.String
8
9
10loose: TypedDataFrame[Loose]
Cause. The declared columns of a frame are complete. A Columns class cannot leave room for other columns.
Fix. Delete closed=False. To accept other columns, declare a remainder with other_columns(), and write it as the second argument: TypedDataFrame[Loose, Rest].
A schema declares an optional column.
schemas.pymypy: 2 errors
1import polars as pl
2
3from astrolabe import Columns, TypedDataFrame
4
5
6class Prices(Columns):
7 symbol: pl.String
8 price: float
↑error: Column "price" must be a polars dtype or an astrolabe dtype marker [columns-field-type]
9
10
11class Partial(Columns, total=False):
↑error: Columns subclass "Partial" must not use total=False, Required[...], or NotRequired[...]: every column is required [columns-not-total]
12 symbol: pl.String
13
14
15prices: TypedDataFrame[Prices]
16partial: TypedDataFrame[Partial]
Cause. A Polars column is never optional: a frame has it, or it does not. total=False, Required[...] and NotRequired[...] have no meaning for a frame.
Fix. Delete the marker. For a function that accepts frames with and without a column, declare the column in a remainder promise or use two schemas.
A concat combines columns differently for some callers.
example.pymypy: 1 error
1import polars as pl
2
3from astrolabe import Columns, TypedDataFrame, other_columns
4
5
6class Keys(Columns):
7 symbol: pl.String
8
9
10class Priced(Columns):
11 symbol: pl.String
12 price: pl.Float64
13
14
15Rest = other_columns()
16
17
18def stack(left: TypedDataFrame[Keys, Rest], right: TypedDataFrame[Priced]) -> None:
19 pl.concat([left, right], how="diagonal")
↑error: This concat combines columns differently for some callers: another frame's remainder can hold "price". Exclude the names with without=/without_prefix= [concat-collision]
↑note: set allow_unproven = "possible" in [tool.astrolabe] to allow this, or set [tool.astrolabe.allow_unproven_overrides] concat = "possible"
Cause. A diagonal concat lines up the columns of each frame by name. The remainder of one frame can hold a name that another frame declares, so the set of shared names depends on the caller.
Fix. Exclude the names from the remainder with without= or without_prefix=.
Level. An unproven result. It passes at the level possible and every less strict level.
An operation gives two columns with one name.
example.pymypy: 1 error
1class Trades(Columns):
2 symbol: pl.String
3 price: pl.Float64
4 quantity: pl.Int64
5
6
7trades: TypedDataFrame[Trades]
8trades.select("price", pl.col("quantity").alias("price"))
↑error: select would produce duplicate column "price" [duplicate-column]
Cause. Polars refuses a frame with two columns of one name.
Fix. Give one of the columns another name with .alias(...).
An argument does not fit its declaration.
features.pymypy: 2 errors
1import polars as pl
2
3from astrolabe import Columns, TypedDataFrame, column
4
5
6class Trades(Columns):
7 symbol: pl.String
8 price: pl.Float64
9 quantity: pl.Int64
10
11
12trades: TypedDataFrame[Trades]
13Price = column("price", pl.Float64)
14
15
16def discounted(price: Price) -> Price:
17 return price * 0.9
18
19
20trades.with_columns(discounted(pl.col("price")))
21discounted(pl.col("quantity"))
↑error: Argument "price" does not match its declared type: it uses column "quantity", which the declared type does not allow [expression-fit]
22discounted(pl.col("price").sum())
↑error: Argument "price" does not match its declared type: it combines rows into one value, but the declared type works row by row [expression-fit]
Cause. An expression argument reads a column that the declaration does not allow, gives another data type, or treats rows in another way: row by row, as an aggregation, or as a literal. The message names the first part that fails. The same code reports an argument that is not a typed expression.
Fix. Pass an expression that fits, or change the declaration. See Declare expression types.
Level. A fault of fit is always an error. An argument that is not a typed expression is an unproven result: it passes at the level asserted.
A frame breaks a promise of the remainder.
notional.pymypy: 1 error
1class Trades(Columns):
2 symbol: pl.String
3 price: pl.Float64
4 quantity: pl.Int64
5
6
7Clean = other_columns(without=["notional"])
8
9
10def add_notional(df: TypedDataFrame[Trades, Clean]) -> TypedDataFrame[Trades, Clean]:
11 return df
12
13
14class Booked(Trades):
15 notional: pl.Float64
16
17
18booked: TypedDataFrame[Booked]
19add_notional(booked)
↑error: Remainder "Clean" must not contain column "notional" [forbidden-column]
Cause. A parameter declares a remainder with a promise, for example other_columns(without=["notional"]). The frame of the caller breaks it: it holds an excluded name or data type.
Fix. Pass a frame that keeps the promise, for example with drop(...) first. See Open frames.
Level. A frame that breaks the promise is always an error. An open frame that cannot prove the promise is an unproven result: it passes at the level asserted.
A runtime schema call gets an open frame type.
boundary.pymypy: 1 error
1import polars as pl
2
3from astrolabe import Columns, TypedDataFrame, check_schema, other_columns
4
5
6class Trades(Columns):
7 symbol: pl.String
8 price: pl.Float64
9
10
11Rest = other_columns()
12
13raw = pl.read_parquet("trades.parquet")
14trades = check_schema(raw, TypedDataFrame[Trades, Rest])
↑error: check_schema() needs a closed frame type, and this frame type has undeclared columns. Pass a frame type with a schema class and no remainder, for example TypedDataFrame[MyColumns] [frame-type-not-closed]
Cause. check_schema, force_schema and assume_schema give a frame the type that their second argument names. The undeclared columns of an open frame have no data type, so the call cannot check or cast them. A frame type with no schema class declares no column.
Fix. Pass a frame type with a schema class and no remainder, for example TypedDataFrame[Trades].
An inferred return type falls back to the plain type.
example.pymypy: 1 error
1import polars as pl
2
3from astrolabe import InferredExpr, infer
4
5
6def scaled(price: pl.Expr, factor: float) -> InferredExpr:
7 return infer(price * factor)
8
9
10scaled(pl.col("price"), 2.0)
↑error: One of this function's infer(...) variants is untyped, so its whole return type falls back to the plain, erased type instead of InferredExpr's own union (set disallow_implicit_inferred_fallback = false in [tool.astrolabe] to allow this, or set [tool.astrolabe.disallow_implicit_inferred_fallback_overrides] expr = false to allow just this placeholder) [implicitly-inferred-fallback]
Cause. One value in infer(...) is a plain type that the checker does not track, so the whole return type of the function is the plain type. This is an error only with the setting disallow_implicit_inferred_fallback.
Fix. Type the value, for example with a declaration on the parameter. See Inferred return types.
A column of the result has no proven data type.
pipeline.pymypy: 1 error
1class Trades(Columns):
2 symbol: pl.String
3 price: pl.Float64
4 quantity: pl.Int64
5
6
7trades: TypedDataFrame[Trades]
8rounded = trades.with_columns(pl.col("price").map_elements(round))
↑error: Column "price" of "map_elements" is Unchecked: a Python function decides the type, and no return_dtype= names it [implicitly-unchecked]
↑note: set allow_unproven = "omitted" in [tool.astrolabe] to allow this, or set [tool.astrolabe.allow_unproven_overrides] map_elements = "omitted"
Cause. The operation knows the name of the column, and cannot prove its data type, for example after map_elements without return_dtype=. The rest of the result keeps its schema.
Fix. State the data type, for example with return_dtype=. See Python functions and Unchecked.
Level. The note under the error names the level that lets it pass. It depends on the reason.
The result of an operation has no known schema.
features.pymypy: 1 error
1class Priced(Columns):
2 symbol: pl.String
3 price: pl.Float64
4 quantity: pl.Int64
5
6
7Rest = other_columns()
8
9
10def by_venue(df: TypedDataFrame[Priced, Rest]) -> None:
11 df.group_by("venue").agg(pl.len())
↑error: The result of "group_by" has no known schema: the undeclared columns of the frame can hold the name, or not [implicitly-untyped]
↑note: set allow_unproven = "possible" in [tool.astrolabe] to allow this, or set [tool.astrolabe.allow_unproven_overrides] group_by = "possible"
Cause. The operation cannot prove the schema of its result, so it gives the plain frame. The message states the reason.
Fix. Remove the reason: declare the column that the operation reads, write a name as a literal, or give a schema. The note names the level that lets the error pass.
Level. The note under the error names the level that lets it pass. It depends on the reason.
infer() or an inferred type has no effect here.
prices.pymypy: 2 errors
1import polars as pl
2
3from astrolabe import InferredDataFrame, infer
4
5
6def doubled(price: pl.Expr) -> pl.Expr:
7 return infer(price * 2)
↑error: infer() has no effect here: the enclosing function's return type is not InferredExpr/InferredDataFrame/InferredLazyFrame [infer-misuse]
8
9
10def frame() -> InferredDataFrame:
11 return infer(pl.col("price"))
12
13
14frame()
↑error: infer(...) variant is an expression, which is not a valid variant of InferredDataFrame [wrong-inferred-variant]
Cause. infer(...) is in a function whose return type is not an inferred one, is not the whole value of a return, or an inferred type is in a place other than the return type.
Fix. Use infer(...) around the whole value of each return in a function with an inferred return type. See Inferred return types.
Polars refuses the call whatever the data.
example.pymypy: 1 error
1class Trades(Columns):
2 symbol: pl.String
3 price: pl.Float64
4 quantity: pl.Int64
5
6
7trades: TypedDataFrame[Trades]
8trades.explode([])
↑error: explode() with no columns to explode always raises [invalid-call-shape]
Cause. The form of the arguments makes the call fail at run time for any data, for example .explode() with no columns.
Fix. Correct the arguments as the message says.
A cast can never succeed.
expressions.pymypy: 3 errors
1from typing import Literal
2
3import polars as pl
4
5from astrolabe import Columns, Datetime, TypedDataFrame
6
7
8class Fills(Columns):
9 symbol: pl.String
10 time: Datetime[Literal["us"], None]
11
12
13fills: TypedDataFrame[Fills]
14fills.select(pl.col("symbol").round(2))
↑error: "symbol" has no result type at this frame's column types: round does not accept String, and polars raises InvalidOperationError [unsupported-dtype-op]
15fills.select(pl.col("time").cast(pl.Boolean))
↑error: This expression cannot cast from Datetime(time_unit='us', time_zone=None) to Boolean [invalid-cast]
16fills.select(pl.col("symbol") + pl.col("time"))
↑error: "symbol" has no result type at this frame's column types: Plus not allowed on String and Datetime(time_unit='us', time_zone=None), and polars raises InvalidOperationError [unsupported-dtype-op]
Cause. Polars cannot cast the data type of the source to the target, for any value.
Fix. Cast to another data type, or convert the column with a method that fits it, for example dt.epoch() for a Datetime.
The keys of a join have data types that do not join.
pipeline.pymypy: 3 errors
1class Trades(Columns):
2 symbol: pl.String
3 price: pl.Float64
4 quantity: pl.Int64
5
6
7trades: TypedDataFrame[Trades]
8venues = pl.read_csv(
9 "venues.csv",
10 schema={"symbol": pl.Int64, "venue": pl.String},
11)
12trades.filter(pl.col("price"))
↑error: Column "price" is not Boolean, cannot .filter() [wrong-column-shape]
13trades.with_columns(pl.col("symbol") - pl.col("price"))
↑error: "symbol" has no result type at this frame's column types: arithmetic on dtypes String and Float64 is not allowed, and polars raises InvalidOperationError [unsupported-dtype-op]
14trades.join(venues, on="symbol")
↑error: Join key "symbol" is String on the left and Int64 on the right [join-key-dtype]
Cause. The two sides of a join key have data types that Polars does not join, for example a String and an Int64.
Fix. Cast one key to the data type of the other before the join.
A join renames columns for some callers and not for others.
enrich.pymypy: 1 error
1class Keys(Columns):
2 symbol: pl.String
3
4
5Left = other_columns()
6Right = other_columns()
7
8
9def enrich(left: TypedDataFrame[Keys, Left], right: TypedDataFrame[Keys, Right]) -> None:
10 left.join(right, on="symbol")
↑error: This join renames columns for some callers but not others: the two remainders can share a name. Declare disjoint_from= on one of them [join-rename]
↑note: set allow_unproven = "possible" in [tool.astrolabe] to allow this, or set [tool.astrolabe.allow_unproven_overrides] join = "possible"
Cause. A join adds a suffix to a right column whose name is also a left column. The remainders can hold a shared name, so the rename depends on the caller.
Fix. Declare disjoint_from= on one remainder, or without_suffix= on the left one, as the message says. See Open frames.
Level. An unproven result. It passes at the level possible and every less strict level.
A declaration breaks a rule of form.
features.pymypy: 2 errors
1import polars as pl
2
3from astrolabe import column, typed_expr
4
5NoRead = typed_expr(gives=pl.Int64, named="n")
↑error: Malformed declaration: no column is read. Read one, name a typed_agg, or use typed_lit [malformed-declaration]
6Pair = typed_expr(column("price", pl.Float64), column("quantity", pl.Int64), gives=pl.Float64)
↑error: Malformed declaration: named= is required with two or more reads [malformed-declaration]
Cause. A declaration of an expression breaks one of its rules, for example it reads no column, reads one column two times, or has rows that overlap. The checker reports it at the declaration, never at a call.
Fix. Correct the declaration as the message says. See Declare expression types.
A type argument of a marker has the wrong form.
example.pymypy: 1 error
1import polars as pl
2
3from astrolabe import Columns, TypedDataFrame
4
5
6class Keys(Columns):
7 symbol: pl.String
8
9
10frame: TypedDataFrame[Keys, int]
↑error: TypedDataFrame[...]/TypedLazyFrame[...]'s second argument must be a remainder: a class declared with other_columns(...), a remainder marker over one, or SplitColumns[...] [marker-arg]
Cause. A marker such as TypedDataFrame, Minus or Datetime takes arguments of a fixed form. The message names the form.
Fix. Write the argument in the form that the message gives.
A marker has the wrong number of type arguments.
example.pymypy: 2 errors
1import polars as pl
2
3from astrolabe import Columns, Prefixed, TypedDataFrame, other_columns
4
5
6class Keys(Columns):
7 symbol: pl.String
8
9
10Rest = other_columns()
11frame: TypedDataFrame[Keys, Prefixed[Rest]]
↑error: TypedDataFrame[...]/TypedLazyFrame[...]'s second argument must be a remainder: a class declared with other_columns(...), a remainder marker over one, or SplitColumns[...] [marker-arg]
↑error: Prefixed[...] requires exactly 2 type arguments: Prefixed[prefix, name_expr] [marker-arity]
Cause. A marker takes a fixed number of type arguments, and this one has more or fewer.
Fix. Write the number of arguments that the message gives.
A frame argument lacks a column that the parameter needs.
features.pymypy: 1 error
1class Priced(Columns):
2 symbol: pl.String
3 price: pl.Float64
4 quantity: pl.Int64
5
6
7class Turnover(Columns):
8 symbol: pl.String
9 turnover: pl.Float64
10
11
12Rest = other_columns()
13
14
15def turnover(df: TypedDataFrame[Priced, Rest]) -> TypedDataFrame[Turnover]:
16 value = (pl.col("price") * pl.col("quantity")).sum()
17 return df.group_by("symbol").agg(value.alias("turnover"))
18
19
20class Blotter(Priced):
21 venue: pl.String
22
23
24class Quotes(Columns):
25 symbol: pl.String
26 price: pl.Float64
27
28
29blotter: TypedDataFrame[Blotter]
30quotes: TypedDataFrame[Quotes]
31turnover(blotter)
32turnover(quotes)
↑error: Missing required column "quantity" [missing-column]
Cause. The parameter declares a column, and the frame of the caller does not have it.
Fix. Pass a frame with the column, for example with with_columns(...) first.
The frames of a stack or a concat do not agree.
example.pymypy: 1 error
1class Trades(Columns):
2 symbol: pl.String
3 price: pl.Float64
4 quantity: pl.Int64
5
6
7trades: TypedDataFrame[Trades]
8
9
10class Quotes(Columns):
11 symbol: pl.String
12 price: pl.Float64
13
14
15quotes: TypedDataFrame[Quotes]
16trades.vstack(quotes)
↑error: .vstack() requires an identical schema [schema-mismatch]
Cause. vstack, extend, merge_sorted and a vertical or relaxed pl.concat need the same columns and data types in each frame. A diagonal concat needs one data type for each shared column.
Fix. Give the frames the same schema first, for example with select(...) and cast(...).
A selection cannot take this rename.
selections.pymypy: 1 error
1class Trades(Columns):
2 symbol: pl.String
3 price: pl.Float64
4 quantity: pl.Int64
5
6
7trades: TypedDataFrame[Trades]
8doubled = trades.with_columns((cs.numeric() * 2).name.suffix("_x2"))
9trades.select(cs.numeric().alias("number"))
↑error: A selection has several outputs, which .alias() cannot rename: use .name.suffix() or .name.prefix() [selection-rename]
Resultdoubleddeclaredadded
symbolStringpriceFloat64quantityInt64price_x2Float64quantity_x2Int64
Revealed type
TypedDataFrame[TypedDict({'symbol': String, 'price': Float64, 'quantity': Int64, 'price_x2': Float64, 'quantity_x2': Int64}, closed=True), None]
Cause. A selection can pick several columns, and .alias(...) gives one name.
Fix. Use .name.suffix(...) or .name.prefix(...). See Selections.
The checker cannot prove that a column exists.
venues.pymypy: 1 error
1class Trades(Columns):
2 symbol: pl.String
3 price: pl.Float64
4 quantity: pl.Int64
5
6
7Rest = other_columns()
8
9
10def by_venue(df: TypedDataFrame[Trades, Rest]) -> None:
11 df.select("symbol", "venue")
↑error: Column "venue" is not declared and cannot be proven to exist [undeclared-column]
↑note: set allow_unproven = "possible" in [tool.astrolabe] to allow this, or set [tool.astrolabe.allow_unproven_overrides] select = "possible"
Cause. The frame does not declare the name, and its remainder can hold the name or not, depending on the caller. The operation then fails at run time for at least one caller.
Fix. Declare the column in the schema of the parameter. Then every caller must pass it.
Level. An unproven result. It passes at the level possible and every less strict level.
The frame has no column of this name.
pipeline.pymypy: 1 error
1import polars as pl
2
3trades = pl.read_csv(
4 "trades.csv",
5 schema={
6 "symbol": pl.String,
7 "price": pl.Float64,
8 "quantity": pl.Int64,
9 },
10)
11trades.select("symbol", "prcie")
↑error: Unknown column "prcie" [unknown-column]
Cause. The checker proves that the frame cannot hold the name: the frame is closed, or its remainder cannot hold the name.
Fix. Correct the name, or add the column in an earlier step.
An operation has no result at these data types.
pipeline.pymypy: 3 errors
1import polars as pl
2
3from astrolabe import Columns, TypedDataFrame
4
5
6class Trades(Columns):
7 symbol: pl.String
8 price: pl.Float64
9 quantity: pl.Int64
10
11
12trades: TypedDataFrame[Trades]
13trades.select("venue")
↑error: Unknown column "venue" [unknown-column]
14trades.select(pl.col("symbol") - pl.col("price"))
↑error: "symbol" has no result type at this frame's column types: arithmetic on dtypes String and Float64 is not allowed, and polars raises InvalidOperationError [unsupported-dtype-op]
15trades.filter(pl.col("price"))
↑error: Column "price" is not Boolean, cannot .filter() [wrong-column-shape]
Cause. An operator or a method has no result for the data types of its inputs, for example String - Float64. Polars raises at run time.
Fix. Cast the inputs to data types that the operation accepts.
A vertical concat is not allowed.
example.pymypy: 1 error
1class Trades(Columns):
2 symbol: pl.String
3 price: pl.Float64
4 quantity: pl.Int64
5
6
7trades: TypedDataFrame[Trades]
8pl.concat([trades, trades])
↑error: pl.concat(how="vertical") is disallowed: column order cannot be verified for a schema derived through a Rest-generic function (set disallow_vertical_concat = false in [tool.astrolabe] to allow it as an untyped result instead) [vertical-concat-disallowed]
Cause. A vertical concat stacks frames by the position of their columns, and a type does not hold the order of the columns. This is an error only with the setting disallow_vertical_concat.
Fix. Use how="diagonal", which lines up the columns by name.
A column is not in the family that the operation needs.
operations.pymypy: 1 error
1import polars as pl
2
3from astrolabe import Columns, List, TypedDataFrame
4
5
6class Baskets(Columns):
7 basket: pl.String
8 symbols: List[pl.String]
9
10
11baskets: TypedDataFrame[Baskets]
12exploded = baskets.explode("symbols")
13baskets.explode("basket")
↑error: Column "basket" is not List/Array, cannot explode [wrong-column-shape]
Resultexplodeddeclared
basketStringsymbolsString
Revealed type
TypedDataFrame[TypedDict({'basket': String, 'symbols': String}, closed=True), None]
Cause. The operation needs a family of data types, for example .explode() needs a List or an Array, and .filter() needs a Boolean.
Fix. Pass a column of that family, or convert the column first.
A frame argument has a declared column with another data type.
example.pymypy: 1 error
1import polars as pl
2
3from astrolabe import Columns, TypedDataFrame, other_columns
4
5
6class Trades(Columns):
7 symbol: pl.String
8 quantity: pl.Int64
9
10
11class Rounded(Columns):
12 symbol: pl.String
13 quantity: pl.Float64
14
15
16Rest = other_columns()
17
18
19def total(df: TypedDataFrame[Trades, Rest]) -> None: ...
20
21
22rounded: TypedDataFrame[Rounded]
23total(rounded)
↑error: Column "quantity" has the wrong dtype [wrong-dtype]
Cause. The parameter is an open frame that declares a column, and the frame of the caller has the column with another data type. For a closed frame parameter, mypy reports the same fault as [arg-type].
Fix. Cast the column to the declared data type before the call.
A returned value is not of the declared kind.
prices.pymypy: 2 errors
1import polars as pl
2
3from astrolabe import InferredDataFrame, infer
4
5
6def doubled(price: pl.Expr) -> pl.Expr:
7 return infer(price * 2)
↑error: infer() has no effect here: the enclosing function's return type is not InferredExpr/InferredDataFrame/InferredLazyFrame [infer-misuse]
8
9
10def frame() -> InferredDataFrame:
11 return infer(pl.col("price"))
12
13
14frame()
↑error: infer(...) variant is an expression, which is not a valid variant of InferredDataFrame [wrong-inferred-variant]
Cause. A function with InferredExpr must return expressions, and a function with InferredDataFrame or InferredLazyFrame must return frames of that kind.
Fix. Return a value of the declared kind, or change the return type.