12. Troubleshooting
- Category: Tutorial
What you will learn
- The common error messages, exactly as KDB prints them.
- What each one means and how to fix it.
- Answers to frequent questions.
All messages below are real output (examples/12-troubleshooting/). Use Ctrl+F and search for words from your error.
Quick table
| Message starts with | Section |
|---|---|
the database is open in another program | Database is locked |
a row with ... already exists | Duplicate key |
unknown column | Unknown column |
table or view ... does not exist | Unknown table |
expected ... but found ... | Syntax error |
is not a time like / cannot compare TIMESTAMP | Time format |
has more than 2 decimal places | Decimal places |
has no matching row in | REFERENCES failure |
cannot remove ... still refers to it | Cannot delete a referenced row |
cannot be NULL / needs a value | Missing value |
is out of range | Number out of range |
cannot compare STRING ... with LONG | Type mismatch |
port ... is already in use | Port in use |
access token required / wrong access token (HTTP 401) | Token 401 |
cross-site requests are not allowed (HTTP 403) | 403 cross-site |
this SQL changes data, so it must be sent with POST (HTTP 405) | 405 GET changes data |
???? or broken letters instead of non-English text | Text in other languages looks broken |
failed its checksum, DAMAGED | Damage |
Database is locked
error: Locked: the database is open in another program (for example kdb serve). Stop it, or query it through that server's console or HTTP API (kdb serve)
exit code: 1
error: the database is open in another program. Stop it first, or run BACKUP DATABASE in that server's console or API.
exit code: 1
(The first line is from kdb sql, the second from kdb backup, while kdb serve had the database open.)
Why: only one program can open a database folder. kdb serve, kdb shell or another kdb sql has it open.
Fix:
- Use the running server: the console,
POST /exec,POST /api/backup,POST /api/check(chapter 8). - Or stop the other program (Ctrl+C in its window), then run your command.
- Look for a forgotten
kdb shellin another window. In PowerShell:Get-Process kdb. - Do not delete
kdb.lock. The lock is held by the running program, not by the file. When no program runs, the lock is free.
Duplicate key
INSERT INTO ochl_day VALUES ('2026-09-10', 'CITY', 504.6, 507, 500.7, 504.9, 183040);
error: a row with symbol='CITY', time='2026-09-10T00:00:00.000Z' already exists in 'ochl_day'. Use INSERT OR REPLACE to overwrite it, or INSERT OR IGNORE to skip it
Why: the table has a DEDUP KEY or PRIMARY KEY, and a row with these values is already there (or twice in your statement). Nothing from this INSERT was saved.
Fix: INSERT OR REPLACE (new data wins) or INSERT OR IGNORE (old data wins). If you did not expect a duplicate: check the time zone (+05:45 missing?) and ROUND on the time column (chapter 3).
Unknown column
SELECT time, symbol, clsoe
FROM ochl_day
WHERE symbol = 'CITY';
error: line 1, column 22: unknown column 'clsoe' (table 'ochl_day' has: time symbol open high low close volume)
Why: a typing mistake, or the column is in another table.
Fix: look at the list in the message. With joins, write table.column. In the console, click the arrow next to a table to see its columns.
line 1, column 22 is the place in the SQL you sent. In a file, it is the line of the file.
Unknown table
SELECT * FROM ochl_days;
error: line 1, column 15: table or view 'ochl_days' does not exist
Fix: SHOW TABLES or .tables in the shell. Check that you opened the right database folder: kdb sql creates a new empty database when the folder does not exist, so a wrong folder name gives "does not exist" for every table.
Syntax error
SELECT symbol close FROM ochl_day WHERE;
error: line 1, column 40: expected an expression (a value, column name, function call or '(') but found ';'
Why: the SQL does not follow the grammar. The message says what KDB expected at that place.
Watch out: SELECT symbol close is not an error. It means "column symbol, named close". A missing comma changes your column names quietly.
An unclosed text:
SELECT * FROM brokers WHERE name = 'Northgate;
error: line 1, column 36: expected a closing ' to end the string that starts here but found the end of the input
A reserved word as a name: see chapter 11 ("order").
Time format
INSERT INTO ochl_day VALUES ('10/09/2026', 'CITY', 1, 1, 1, 1, 1);
error: column 'time' (TIMESTAMP): '10/09/2026' is not a time like '2026-09-11 11:00:00' (UTC) or '2026-09-11T11:00:00+05:45'
SELECT time, close FROM ochl_day WHERE time >= '2026-9-1';
error: line 1, column 45: cannot compare TIMESTAMP '2025-12-28T00:00:00.000Z' with STRING '2026-9-1'
Fix: always YYYY-MM-DD with two digits for month and day: '2026-09-01'. Add +05:45 for Nepal times. Convert other formats in your script before sending (for example in PowerShell: ([datetime]::ParseExact('10/09/2026', 'dd/MM/yyyy', $null)).ToString('yyyy-MM-dd')).
Times are 5:45 hours wrong? You wrote Nepal time without +05:45. KDB read it as UTC. Fix the data with an UPDATE: UPDATE t SET time = time - 20700000 WHERE ... (this subtracts 5 h 45 min), but check the DEDUP KEY first.
Decimal places
CREATE TABLE wallet (id INT PRIMARY KEY, balance DECIMAL(2));
INSERT INTO wallet VALUES (1, 100.50);
UPDATE wallet SET balance = balance * 1.015 WHERE id = 1;
table 'wallet' created
1 row(s) inserted
error: column 'balance' (DECIMAL(2)): 102.0075 has more than 2 decimal places
Why: KDB never rounds money by itself.
Fix: round it yourself, the way your business rule says:
UPDATE wallet SET balance = round(balance * 1.015, 2) WHERE id = 1;
SELECT id, balance FROM wallet;
1 row(s) updated
id | balance
---+--------
1 | 102.01
(1 row, 0.1 ms)
For amounts with more than about 15 digits, send the value as text ('123456789012345.67'). A number that big cannot be stored exactly as a DOUBLE, so KDB refuses it and tells you to write it as text.
REFERENCES failure
INSERT INTO trade_log (date, transaction_id, symbol, buyer, seller, quantity, rate)
VALUES ('2026-09-10', 200001, 'CITY', 77, 58, 10, 515);
error: column 'buyer' = '77' has no matching row in brokers(id)
Fix: insert the missing row first (INSERT INTO brokers VALUES (77, '...')), or correct the value. For a big import, load the parent table (brokers) before the child table (trade_log).
Cannot delete a referenced row
DELETE FROM brokers WHERE id = 45;
error: cannot remove brokers(id) = '45': table 'trade_log' column 'buyer' still refers to it (foreign key 'trade_log_buyer_fkey')
Why: trades still point to this broker. A plain REFERENCES never deletes them for you. (A foreign key written with ON DELETE CASCADE or ON DELETE SET NULL would; chapter 22.)
Fix: delete or change the trades first, or keep the broker. Changing the broker's id with UPDATE is refused for the same reason. Dropping the whole table gives: cannot drop table 'brokers': column 'buyer' of table 'trade_log' REFERENCES it; drop that column or table first.
Missing value
INSERT INTO brokers (id) VALUES (99);
error: column 'name' (STRING): cannot be NULL
INSERT INTO ochl_day (symbol, close) VALUES ('CITY', 505);
error: the time column 'time' needs a value
Fix: give a value for NOT NULL and PRIMARY KEY columns and for the time column of a time-series table.
Number out of range
INSERT INTO brokers VALUES (3000000000, 'Too Big');
error: column 'id' (INT): 3000000000 is out of range (-2147483647 to 2147483647)
Fix: use a bigger type (LONG). For an existing table: ALTER TABLE t ALTER COLUMN c TYPE LONG (chapter 3). A column that is the time column or part of SORT BY or DEDUP KEY cannot change its type.
Type mismatch
SELECT * FROM brokers WHERE name = 58;
error: line 1, column 34: cannot compare STRING 'Keystone Securities' with LONG '58'
SELECT sum(symbol) FROM ochl_day;
error: line 1, column 8: sum needs numbers, got 'CITY'
Fix: compare text with 'text' and numbers with numbers. Use CAST when needed.
Other query errors
| Message | Fix |
|---|---|
SAMPLE BY needs a table with a time column (a TIMESERIES table) | SAMPLE BY only works on time-series tables. Use GROUP BY. |
column 'close' is used by a window function in a query with aggregates, so it must be a key of the groups: add it to the SELECT list or to GROUP BY | With GROUP BY or SAMPLE BY, a window function reads the groups, not the rows. Give it an aggregate (sma(last(close), 2)) or a group key, or aggregate in a subquery and put the indicator outside (chapter 5). |
'sma' works over a series of rows and can only be used in a SELECT list | Not in WHERE and not inside another series function. Use a subquery. |
column 'amount' is computed and cannot be set | Computed columns (AS (...)) are not stored. Change the columns it is computed from. |
in subquery: ... unknown column 's.symbol' | The name is wrong, or s is not a table of the outer query. Correlated subqueries work (chapter 23). |
subquery returned 3 rows; a value needs exactly one (add LIMIT 1?) | A (SELECT ...) used as a value must return one row. |
time zone '...' is not supported | Use 'Asia/Kathmandu' or '+05:45'. |
offset '00:15' must look like '+05:45' or '-03:00' | Write WITH OFFSET '+00:15'. |
DEDUP KEY must include the time column | Add the time column to the DEDUP KEY. |
there is no unique key on (a, b) of table 't' for ON CONFLICT | Add PRIMARY KEY, UNIQUE (a, b) or a UNIQUE INDEX on exactly these columns (chapter 23). |
ON CONFLICT DO UPDATE cannot change the same row twice in one statement | Two new rows have the same key. Remove the copy, or use DO NOTHING. |
the query makes more than N groups (max_groups) | The GROUP BY makes more groups than --max-groups allows. Add a time filter or group by fewer columns. (A GROUP BY that only needs much memory does not fail: it uses temporary files and is slower.) |
table 'x' is busy: ..., database is busy: ..., deadlock: ... | Another transaction holds the table. Nothing of your statement was done. Wait a little and run the whole transaction again (chapter 15). |
The outputs of these are in examples/05-queries/, examples/10-migrating-from-questdb/, examples/11-sql-reference/ and examples/03-tables/.
Port in use
error: Already exists: port 9945 on 127.0.0.1 is already in use
exit code: 1
Why: another program (often another kdb serve) listens on that port.
Fix: use another port (--port 9901), or find the program:
Get-NetTCPConnection -LocalPort 9900 -State Listen | Select-Object OwningProcess
Get-Process -Id <OwningProcess>
Token 401
{"error":"access token required"} (HTTP 401)
{"error":"wrong access token"} (HTTP 401)
Why: the server was started with --token.
Fix: send Authorization: Bearer <token>. In the console, enter the token in the dialog. If the console keeps asking, the saved token is wrong: clear the site data of 127.0.0.1:9900 in the browser, or open a private window. Check with curl.exe -s http://127.0.0.1:9900/health: "auth_required":true means a token is needed.
403 cross-site, cross-origin, host name
{"error":"cross-site requests are not allowed"} (HTTP 403)
{"error":"cross-origin requests are not allowed"} (HTTP 403)
{"error":"host name 'market.example.com' is not allowed without an access token; use localhost or start the server with --token"} (HTTP 403)
Why: protection against web pages that try to use your local KDB (chapter 8).
Fix:
- Call KDB from a program or your backend, not from JavaScript on another web site.
- Open the console with
http://127.0.0.1:9900orhttp://localhost:9900. - Behind a reverse proxy or with a host name: start the server with
--token.
405 GET changes data
{"error":"this SQL changes data, so it must be sent with POST /exec (GET /exec only reads)"} (HTTP 405)
{"error":"CSV export needs a SELECT, SHOW or EXPLAIN statement"} (HTTP 400)
Why: GET /exec only runs reading statements, and /exp only exports reading statements. A web page could otherwise change your data with a simple link.
Fix: send INSERT, UPDATE, DELETE, CREATE, DROP, ALTER and BACKUP DATABASE with POST: curl.exe http://127.0.0.1:9900/exec --data-binary "INSERT ...".
Text in other languages looks broken
kdb sql market "CREATE TABLE IF NOT EXISTS names_np (symbol SYMBOL PRIMARY KEY, name_np STRING)"
# Text in any language (here Nepali) works directly on the command line:
kdb sql market "INSERT OR REPLACE INTO names_np VALUES ('VOLT', 'भोल्ट रिभर')"
kdb sql market "SELECT symbol, name_np FROM names_np"
# It also works from a UTF-8 file (or over HTTP):
$OutputEncoding = New-Object System.Text.UTF8Encoding $false
"INSERT OR REPLACE INTO names_np VALUES ('VOLT', 'भोल्ट रिभर');" | Set-Content np.sql -Encoding UTF8
Get-Content np.sql -Raw -Encoding UTF8 | kdb sql market -
kdb sql market "SELECT symbol, name_np FROM names_np"
table 'names_np' created
1 row(s) inserted
symbol | name_np
-------+-----------
VOLT | भोल्ट रिभर
(1 row, 0.2 ms)
0 row(s) inserted, 1 replaced
symbol | name_np
-------+-----------
VOLT | भोल्ट रिभर
(1 row, 0.2 ms)
If you see ????: Windows PowerShell 5.1 sends pipe text as ASCII unless $OutputEncoding is UTF-8. Set it (as above) before you pipe SQL into kdb sql - or kdb shell. Text in command-line arguments and over HTTP is fine.
If the letters look broken only in the console window, the window font may not have Devanagari. Use Windows Terminal, or save the output as CSV and open it in an editor.
Damage
Messages like failed its checksum, DAMAGED, the end of the log is damaged, exit code 2:
- Stop the server and all programs that use the database.
- Copy the whole database folder to a safe place.
kdb check <db>to see the damage.kdb repair <db> --backups <backups>.- If it still fails:
kdb restore <newest backup> <new folder> --logs-from <db>.
Chapter 9 shows a full real example.
The server stopped
If the server process ends without Ctrl+C (crash, closed window, killed), start it again. The banner then shows last shutdown: not clean (crash or kill) and KDB replays the log. Committed data is not lost.
FAQ
Where are my times? They look 5:45 hours early.
KDB shows UTC. 05:15Z is 11:00 in Nepal. The chart in the console shows your local time.
Can two programs write at the same time?
Not to the same folder directly. Run one kdb serve and let all programs use its HTTP API or its PostgreSQL port. In the server, writers of different tables run at the same time. Writers of the same table take turns. A read waits for a long write of the same database unless the server runs with --reader-yield on (the Docker image does), then it runs between the writer's statements (chapter 15).
Is a statement saved when KDB says "inserted"? Yes, it is on the disk. A power cut after that does not lose it.
How do I undo a change?
Inside BEGIN ... COMMIT, ROLLBACK undoes everything since BEGIN (chapter 13). A dropped table comes back with UNDROP TABLE for 7 days (chapter 21). After a committed change, restore a backup to the time before it (kdb restore --until, chapter 9). Make a backup before big changes.
How big can a table be?
There is no fixed limit. Very old data can be removed quickly with ALTER TABLE ... DROP PARTITION OLDER THAN 1 YEAR, or by itself with a TTL (ALTER TABLE ... SET TTL 90 DAYS, chapter 3).
Why is my query slow?
Add a time filter on the time-series table (time IN '2026-09'), select fewer columns, and check with EXPLAIN how many partitions are read (chapter 5).
Can I use DBeaver, psql or Excel to connect?
DBeaver and psql: yes, through the PostgreSQL port 8832 (chapter 14). For Excel, export CSV (kdb sql ... --format csv, or the console's Export CSV).
Does KDB know stock exchange holidays?
No. is_trading_day() only checks for a Sunday-to-Thursday week (as on some exchanges). Keep a holiday table if you need it.
Can I copy the database folder to another PC?
Yes, when no program has it open. Better: kdb backup, copy the backup folder, kdb restore on the other PC. The restore checks every file.
Do Linux and Windows use the same files? The file format is the same on all systems (chapter 1, FORMAT.md).
Where is kdb.conf?
Next to the kdb program (or the file named with --config <file>). It is a small JSON file that the console's Settings page writes. It holds data_dir, wal_archive_mb, trash_keep_hours, keep_versions, and the folders of the console, the help and the Claude skill (web_dir, help_dir, skill_dir). An option on the command line always wins over it. Everything else is a command-line option (kdb with no command lists them).
Try it yourself
- You get
error: line 3, column 5: unknown column 'volum'. What do you do? - A script prints
the database is open in another program, but no server is running. What else can hold the database? - An INSERT of 5000 rows fails with a duplicate key. How many rows were saved?
Answers
- Look at line 3, column 5 of your SQL, and fix the name to
volume(the message lists the real columns). - A
kdb shellorkdb sqlin another window, or a scheduled task (backup script) that is running. Check withGet-Process kdb. - None. One statement is all or nothing. Use
INSERT OR REPLACEorINSERT OR IGNORE, or remove the duplicates.
Next: 13. Functions, procedures and triggers. Back to the index. Also read the known limitations.