No Windows:
tl; dr: Tente abrir o arquivo novamente.
Nosso sistema estava sofrendo desse problema, e definitivamente não era um problema de permissões, já que o próprio programa seria capaz de abrir o banco de dados como gravável a partir de muitos threads na maioria das vezes, mas ocasionalmente (apenas no Windows, não no OSX), um encadeamento obteria esses erros mesmo que todos os outros encadeamentos no programa não estivessem tendo dificuldades.
Por fim, descobrimos que os encadeamentos que estavam falhando eram apenas aqueles que tentavam abrir o banco de dados imediatamente após o encerramento de outro encadeamento (dentro de 3 ms). Especulamos que o problema se devia ao fato de que o Windows (ou a implementação sqlite no Windows) nem sempre limpa imediatamente os recursos do arquivo ao fechar um arquivo. Conseguimos contornar isso executando uma consulta de gravação de teste no banco de dados ao abrir (por exemplo, criando e eliminando uma tabela com um nome bobo). Se a criação / eliminação falhou, esperamos por 50 ms e tentamos novamente, repetindo até que obtivéssemos sucesso ou 5 segundos se passassem.
Funcionou; aparentemente, só precisava haver tempo suficiente para que os recursos fossem transferidos para o disco.